def demo_input_and_output(): input = yield 'what is the input?' yield 'input is: %s' % input gen = demo_input_and_output() print(gen.next()) 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,那么就有效果了










