zoukankan      html  css  js  c++  java
  • 深入理解 Python yield

    https://blog.csdn.net/lftaoyuan/article/details/78915518

    python2和python3是不兼容的,通篇环境都是python3.6

    简单的yield实例

    以前只是粗略的知道yield可以用来为一个函数返回值塞数据,比如下面的例子:

    def addlist(alist):
        for i in alist:
            yield i + 1

    取出alist的每一项,然后把i + 1塞进去。然后通过调用取出每一项:

    alist = [1, 2, 3, 4]
    for x in addlist(alist):
        print(x)

    这的确是yield应用的一个例子,但是,看过很多东西,并自己反复体验后,对yield有了一个全新的理解,其中这篇算是精品了。

    包含yield的函数

    假如你看到某个函数包含了yield,这意味着这个函数已经是一个Generator,它的执行会和其他普通的函数有很多不同。比如下面的简单的函数:

    def h():
        print('study yield')
        yield 5
        print('go on!')
    
    h()

    可以看到,调用h()之后,print 语句并没有执行!这就是yield。具体的内容后面会越来越清晰,包括yield的工作原理。

    yield是一个表达式

    python 2.5以前,yield是一个语句,我也没有考证,因为早都不用了,现在yield是一个表达式:

    m = yield 5

    表达式(yield 5)的返回值将赋值给m,所以,m = 5 肯定是错的。

    那么如何获取(yield 5)的返回值呢?需要用到send(msg)

    yield工作原理

    揭晓yield的工作原理,需要配合next()函数。上面的h()被调用后并没有执行,因为它有yield表达式,通过next()可以恢复Generator执行,直到下一个yield

    def h():
        print('study yield')
        yield 5
        print('go on!')
    
    
    
    c = h()
    d1 = next(c)  # study yield
    d2 = next(c)
    """
    study yield
    go on!
    Traceback (most recent call last):
      File "D:/idea/workspace/pythonSpace/PythonDemo/static/yield_demo.py", line 35, in <module>
        d2 = next(c)
    StopIteration
    """
    • 1
    • 2
    • 3
    • 4

    next()被调用后,h()开始执行,直到遇到yield 5

    因此输出结果是:study yield

    当我们再次调用next()时,会继续执行,直到找到下一个yield。由于后面没有yield了,因此会拋出异常:

    study yield
    go on!
    Traceback (most recent call last):
      File "D:/idea/workspace/pythonSpace/PythonDemo/static/yield_demo.py", line 35, in <module>
        d2 = next(c)
    StopIteration

    send(msg) 与 next()

    了解了next()如何让包含yield的函数执行后,我们再来看另外一个非常重要的函数send(msg)

    其实next()send()在一定意义上作用是相似的

    区别

    send()可以传递yield的值

    next()只能传递None

    所以next() 和 send(None)作用是一样的。

    def s():
        print('study yield')
        m = yield 5
        print(m)
        d = yield 16
        print('go on!')
    
    
    c = s()
    s_d = next(c)  # 相当于send(None)
    c.send('Fighting!')  # (yield 5)表达式被赋予了'Fighting!'

    输出的结果为:

    study yield
    Fighting!

    注意 生成器刚启动时(第一次调用),请使用next()语句或是send(None),不能直接发送一个非None的值,否则会报TypeError,因为没有yield语句来接收这个值。

    send(msg) 与 next()的返回值

    send(msg) 和 next() 的返回值比较特殊,是下一个yield表达式的参数(yield 5,则返回 5)。

    到这里,第一个例子中,通过for i in alist 遍历 Generator,其实是每次都调用了next(),而每次next()的返回值正是yield的参数:

    def s():
        print('study yield')
        m = yield 5
        print(m)
        d = yield 16
        print('go on!')
    
    
    c = s()
    s_d1 = next(c)  # 相当于send(None)
    s_d2 = c.send('Fighting!')  # (yield 5)表达式被赋予了'Fighting!'
    print('My Birth Day:', s_d1, '.', s_d2)

    输出结果:

    study yield
    Fighting!
    My Birth Day: 5 . 16

    中断Generator

    上面的例子中,当没有可执行程序的时候,会抛出一个StopIteration, 开发过程中,中断Generator是一个非常灵活的技巧

    throw

    通过抛出一个GeneratorExit异常来终止Generator。

    close

    close的作用和throw是一样的,看它的源码,可以发现,它和raise一球样

    def throw(self, type, value=None, traceback=None):
        '''Used to raise an exception inside the generator.'''
        # 用于在生成器中抛出一个异常。
        pass
    
    
    def close(self):
        '''Raises new GeneratorExit exception inside the generator to terminate the iteration.'''
        # 在生成器中生成新的GeneratorExit异常来终止迭代。
        pass

    其实最后一个中断生成器可以忽略的,在开发过程中,不可避免的要用到这些,但是Python3内部已经做得很好了,一般不太需要手动去做这件事情。

    demo地址

    https://github.com/seeways/PythonDemo/blob/master/static/yield_demo.py

  • 相关阅读:
    HDU 1269 迷宫城堡
    HDU 4771 Stealing Harry Potter's Precious
    HDU 4772 Zhuge Liang's Password
    HDU 1690 Bus System
    HDU 2112 HDU Today
    HDU 1385 Minimum Transport Cost
    HDU 1596 find the safest road
    HDU 2680 Choose the best route
    HDU 2066 一个人的旅行
    AssetBundle管理机制(下)
  • 原文地址:https://www.cnblogs.com/linkenpark/p/8968734.html
Copyright © 2011-2022 走看看