我很难理解PEP 380。

在什么情况下yield from是有用的? 经典用例是什么? 为什么将其与微线程进行比较?

到目前为止,我使用过生成器,但从未真正使用过协程(由PEP-342引入)。尽管有一些相似之处,生成器和协程基本上是两个不同的概念。理解协程(不仅仅是生成器)是理解新语法的关键。

以我之见,协程是Python中最晦涩的特性,大多数书籍都让它看起来毫无用处和无趣。


感谢这些精彩的回答,但特别感谢agf和他的评论链接到David Beazley的演讲。


当前回答

在异步IO协程的实际使用中,yield from与协程函数中的await具有类似的行为。两者都用于暂停协程的执行。

Yield from由基于生成器的协程使用。 Await用于async def协程。(Python 3.5+开始)

对于Asyncio,如果不需要支持旧的Python版本(即>3.5),async def/await是定义协程的推荐语法。因此,yield from在协程中不再需要。

但一般来说,在asyncio之外,yield from <子生成器>在迭代子生成器时仍有一些其他用途,如前面的回答中所述。

其他回答

简单地说,yield from为迭代器函数提供了尾部递归。

在异步IO协程的实际使用中,yield from与协程函数中的await具有类似的行为。两者都用于暂停协程的执行。

Yield from由基于生成器的协程使用。 Await用于async def协程。(Python 3.5+开始)

对于Asyncio,如果不需要支持旧的Python版本(即>3.5),async def/await是定义协程的推荐语法。因此,yield from在协程中不再需要。

但一般来说,在asyncio之外,yield from <子生成器>在迭代子生成器时仍有一些其他用途,如前面的回答中所述。

Wherever you invoke a generator from within a generator you need a "pump" to re-yield the values: for v in inner_generator: yield v. As the PEP points out there are subtle complexities to this which most people ignore. Non-local flow-control like throw() is one example given in the PEP. The new syntax yield from inner_generator is used wherever you would have written the explicit for loop before. It's not merely syntactic sugar, though: It handles all of the corner cases that are ignored by the for loop. Being "sugary" encourages people to use it and thus get the right behaviors.

这条讨论线程中的消息讨论了这些复杂性:

对于PEP 342引入的附加生成器特性,这是不可能的 更长的情况:正如Greg的PEP中所描述的,简单的迭代不会 正确支持send()和throw()。体操需要支持 当您破坏Send()和throw()时,它们实际上并不复杂 向下,但它们也不是微不足道的。

我不能与微线程进行比较,只能观察到生成器是一种并行。您可以将挂起生成器视为一个线程,它通过yield将值发送给消费线程。实际的实现可能不是这样的(Python开发人员显然对实际的实现非常感兴趣),但这与用户无关。

语法带来的新成果并没有在线程方面为语言增加任何额外的功能,它只是使正确使用现有功能变得更容易。或者更准确地说,它使专家编写的复杂内部生成器的新手更容易通过该生成器,而不会破坏其任何复杂特性。

这段代码定义了一个函数fixed_sum_digits,返回一个枚举所有6位数字的生成器,使得数字和为20。

def iter_fun(sum, deepness, myString, Total):
    if deepness == 0:
        if sum == Total:
            yield myString
    else:  
        for i in range(min(10, Total - sum + 1)):
            yield from iter_fun(sum + i,deepness - 1,myString + str(i),Total)

def fixed_sum_digits(digits, Tot):
    return iter_fun(0,digits,"",Tot) 

试着不屈服地写它。如果你找到了有效的方法,请告诉我。

我认为对于这样的情况:访问树,yield from使代码更简单、更干净。

Yield from生成一个生成器,直到生成器为空,然后继续执行以下代码行。

e.g.

def gen(sequence):
    for i in sequence:
        yield i


def merge_batch(sub_seq):
    yield {"data": sub_seq}

def modified_gen(g, batch_size):
    stream = []
    for i in g:
        stream.append(i)
        stream_len = len(stream)
        if stream_len == batch_size:
            yield from merge_batch(stream)
            print("batch ends")
            stream = []
            stream_len = 0

运行这个程序会得到:

In [17]: g = gen([1,2,3,4,5,6,7,8,9,10])
In [18]: mg = modified_gen(g, 2)
In [19]: next(mg)
Out[19]: {'data': [1, 2]}

In [20]: next(mg)
batch ends
Out[20]: {'data': [3, 4]}

In [21]: next(mg)
batch ends
Out[21]: {'data': [5, 6]}

In [22]: next(mg)
batch ends
Out[22]: {'data': [7, 8]}

In [23]: next(mg)
batch ends
Out[23]: {'data': [9, 10]}

In [24]: next(mg)
batch ends
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
Input In [24], in <cell line: 1>()
----> 1 next(mg)

StopIteration: 

因此,yield from可以从另一个生成器获取输出,做一些修改,然后将自己的输出作为生成器本身提供给其他生成器。

在我看来,这是yield from的主要用例之一