国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Golang > 正文

使用Go語言簡單模擬Python的生成器

2020-04-01 19:13:46
字體:
來源:轉載
供稿:網友
這篇文章主要介紹了使用Go語言簡單模擬Python的生成器,Python的generator是非常酷的功能,用Go實現的代碼也較為簡潔,需要的朋友可以參考下
 
  1. def demo_input_and_output(): 
  2.   input = yield 'what is the input?' 
  3.   yield 'input is: %s' % input 
  4.  
  5. gen = demo_input_and_output() 
  6. print(gen.next()) 
  7. print(gen.send(42)) 
?

這段代碼演示了 python generator 的功能。可以看到 yield 同時做了兩個操作,一個是往外發數據 "waht is the input",同時做的操作是往里收數據 input。而且這個接收數據的操作是一個阻塞的操作,如果外部沒有調用 next() (也就是往里傳遞None),或者調用send(42)(也就是往里傳遞42這個值),那么這個阻塞的操作就會一直等待下去。

也就是說 python 的 generator 自帶了一個對外通信的 channel,用于收發消息。用 go 模擬 python 的 generator 的話寫起來就是這樣的

 

復制代碼代碼如下:
package main

 

import "fmt"

func demoInputAndOutput(channel chan string) {
    channel <- "what is my input?"
    input := <- channel
    channel <- fmt.Sprintf("input is: %s", input)
}

func main() {
    channel := make(chan string)
    go demoInputAndOutput(channel)
    fmt.Println(<- channel)
    channel <- "42"
    fmt.Println(<- channel)
}

 

這段代碼和 python 版本基本上等價。隱含的 channel 在 go 版本里變成顯式的了。yield 變成了 channel <- 操作,同時立馬做了一個 <- channel 的阻塞讀操作。這也就是 yield 的本質吧。

go 的 channel 也可以當成 iterator 被 for 循環使用:

 

復制代碼代碼如下:
package main

 

import "fmt"

func someGenerator() <-chan string {
    channel := make(chan string)
    go func() {
        channel <- "a"
        fmt.Println("after a")
        channel <- "c"
        fmt.Println("after c")
        channel <- "b"
        fmt.Println("after b")
        close(channel)
    }()
    return channel
}

func main() {
    channel := someGenerator()
    for val := range channel {
        fmt.Println(val)
    }
}

 

和 python 的 yield 不同,這里的 channel <- 不等價于 yield,它會往下執行直到阻塞。效果是

 

復制代碼代碼如下:
after a
a
c
after c
after b
b

 

這和預期的順序不一樣。這里沒有把 after a after c after b 都打印出來是因為 channel 默認只有一個元素的buffer,所以寫入了一個就阻塞了。如果增大 buffer,那么就有效果了

 

復制代碼代碼如下:
make(chan string, 10)

 

輸出變成了:
 

  1. after a 
  2. after c 
  3. after b 
?

可見 goroutine 就好象一個獨立的線程一樣自己和自己玩去了,不用等待被執行。如果要模擬 yield 就要加上顯示的同步操作(從 channel 里阻塞讀取信號):

 

復制代碼代碼如下:
package main

 

import "fmt"

func someGenerator() chan string {
    channel := make(chan string)
    go func() {
        channel <- "a"
        <- channel
        fmt.Println("after a")
        channel <- "c"
        <- channel
        fmt.Println("after c")
        channel <- "b"
        <- channel
        fmt.Println("after b")
        close(channel)
    }()
    return channel
}

func main() {
    channel := someGenerator()
    for val := range channel {
        fmt.Println(val)
        channel <- ""
    }
}

 

輸出的結果就是
 

  1. after a 
  2. after c 
  3. after b 
?

到這里我們可以看到,python 的 generator 就好象是 golang 的 goroutine 帶了一個無buffer的channel。這樣導致每次yield一個值,都會產生一次協程上下文切換。雖然協程上下文切換很廉價,但是也不是沒有成本。像 goroutine 的 buffered channel 這樣的設計,可以讓一個 goroutine 一次性多產生一些輸出再阻塞等待,而不是產生一個輸出就阻塞等待一下,再產生另外一個輸出。golang rocks!


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 无锡市| 南江县| 财经| 新蔡县| 洛隆县| 彭州市| 阳谷县| 文登市| 南投市| 辽宁省| 华蓥市| 景泰县| 北安市| 广安市| 含山县| 谷城县| 台东县| 拉萨市| 琼海市| 隆回县| 浮山县| 防城港市| 乳源| 中牟县| 邵阳县| 仁化县| 务川| 黄骅市| 淮北市| 红安县| 衡南县| 永胜县| 淄博市| 介休市| 礼泉县| 灵台县| 荥阳市| 忻城县| 东光县| 措勤县| 得荣县|