• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    使用Go语言简单模拟Python的生成器
    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,那么就有效果了

    复制代码 代码如下:
    make(chan string, 10)

    输出变成了:

    after a
    after c
    after b
    a
    c
    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 - ""
        }
    }

    输出的结果就是

    a
    after a
    c
    after c
    b
    after b
    
    

    到这里我们可以看到,python 的 generator 就好象是 golang 的 goroutine 带了一个无buffer的channel。这样导致每次yield一个值,都会产生一次协程上下文切换。虽然协程上下文切换很廉价,但是也不是没有成本。像 goroutine 的 buffered channel 这样的设计,可以让一个 goroutine 一次性多产生一些输出再阻塞等待,而不是产生一个输出就阻塞等待一下,再产生另外一个输出。golang rocks!

    您可能感兴趣的文章:
    • golang、python、php、c++、c、java、Nodejs性能对比
    • 详解Golang 与python中的字符串反转
    • Golang与python线程详解及简单实例
    • C++、python和go语言实现的简单客户端服务器代码示例
    • Python和GO语言实现的消息摘要算法示例
    • Golang如何调用Python代码详解
    上一篇:Golang记录、计算函数执行耗时、运行时间的一个简单方法
    下一篇:Go语言中字符串的查找方法小结
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    使用Go语言简单模拟Python的生成器 使用,语言,简单,模拟,Python,