代码之家  ›  专栏  ›  技术社区  ›  Charlie Haley

Python:yield和yield赋值

  •  29
  • Charlie Haley  · 技术社区  · 9 年前

    这个代码,包括赋值和产量运算符,是如何工作的?结果相当令人困惑。

    def test1(x): 
        for i in x:
            _ = yield i 
            yield _
    def test2(x): 
        for i in x:
            _ = yield i 
    
    r1 = test1([1,2,3])
    r2 = test2([1,2,3])
    print list(r1)
    print list(r2)
    

    输出:

    [1, None, 2, None, 3, None] 
    [1, 2, 3]
    
    3 回复  |  直到 9 年前
        1
  •  23
  •   Jeremy Brown    9 年前

    赋值语法(“yield表达式”)允许您将生成器视为一个基本的协程。

    首次提出于 PEP 342 并记录在此处: https://docs.python.org/2/reference/expressions.html#yield-expressions

    与生成器一起工作的客户端代码可以使用 send() 方法该数据可通过赋值语法访问。

    发送() 也将迭代-因此它实际上包含 next() 呼叫

    使用您的示例,这是使用couroutine功能的情况:

    >>> def test1(x):
    ...     for i in x:
    ...         _ = yield i
    ...         yield _
    ...
    >>> l = [1,2,3]
    >>> gen_instance = test1(l)
    
    >>> #First send has to be a None
    >>> print gen_instance.send(None)
    1
    >>> print gen_instance.send("A")
    A
    >>> print gen_instance.send("B")
    2
    >>> print gen_instance.send("C")
    C
    >>> print gen_instance.send("D")
    3
    >>> print gen_instance.send("E")
    E
    >>> print gen_instance.send("F")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    

    请注意,由于第二次 yield 在不捕获发送数据的每个循环迭代中。

    编辑: 忘记解释 None 在你的例子中我们让步了。

    从…起 https://docs.python.org/2/reference/expressions.html#generator.next :

    当使用next()方法恢复生成器函数时,当前 yield表达式的计算结果始终为None。

    next() 在使用迭代语法时使用。

        2
  •  11
  •   Community Dan Abramov    7 年前
    _ = yield i 
    yield _
    

    首先是它 yield s引用的值 i ,例如。 1 。然后生成 产量 操作,即 None 。它在循环的每个迭代上执行此操作。

    for i in x:
        _ = yield i
    

    这很简单 产量 s引用的值 ,例如。 1. ,然后进行循环的下一次迭代,产生 2 然后 3 .

    不像 return 这个 产量 关键字可以在表达式中使用:

    x = return 0 # SyntaxError
    x = yield 0 # perfectly fine
    

    现在,当口译员看到 产量 ,它将生成指示值。但是,当执行此操作时,该操作将返回值 没有一个 ,就像 mylist.append(0) print('hello') 回来 价值 没有一个 。当您将该结果分配给引用时,如 _ ,你在保存 没有一个 .

    因此,在第一个片段中,您将生成一个对象,然后保存该对象的“结果” 产量 操作,即 没有一个 ,然后是你 产量 那个 没有一个 。在第二个代码段中,生成一个对象,然后保存该对象的“结果” 产量 但你 从不 产量 那个结果 所以 没有一个 不会出现在输出中。

    请注意 产量 不会总是回来 没有一个 -这就是你发给发电机的 send() 。既然这在本案中不算什么 没有一个 看见 this answer 有关更多信息 发送() .

        3
  •  4
  •   Cyphase    9 年前

    为了进一步解释TigerhawkT3的答案,收益率操作正在回归的原因 None 在你的代码中是因为 list(r1) 没有发送任何内容 进入 发电机。试试看:

    def test1(x):
        for i in x:
            _ = yield i
            yield _
    
    
    r1 = test1([1, 2, 3])
    
    for x in r1:
        print('   x', x)
        print('send', r1.send('hello!'))
    

    输出:

       x 1
    send hello!
       x 2
    send hello!
       x 3
    send hello!
    

    这是一个有点做作的例子,将值发送到生成器可能很有用:

    def changeable_count(start=0):
        current = start
        while True:
            changed_current = yield current
            if changed_current:
                current = changed_current
            else:
                current += 1
    
    counter = changeable_count(10)
    
    for x in range(20):
        print(next(counter), end=' ')
    
    print()
    print()
    
    print('Sending 51, printing return value:', counter.send(51))
    print()
    
    for x in range(20):
        print(next(counter), end=' ')
    
    print()
    print()
    
    print('Sending 42, NOT printing return value')
    print()
    
    counter.send(42)
    
    for x in range(20):
        print(next(counter), end=' ')
    
    print()
    

    输出:

    10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 
    
    Sending 51, printing return value: 51
    
    52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 
    
    Sending 42, NOT printing return value
    
    43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62