Python中连续的两个yield语句是如何工作的?

26

我在合并和拆分部分从pymotw.com发现了这段代码。

from itertools import *


def make_iterables_to_chain():
    yield [1, 2, 3]
    yield ['a', 'b', 'c']


for i in chain.from_iterable(make_iterables_to_chain()):
    print(i, end=' ')
print()
我不太理解make_iterables_to_chain()是如何工作的。它包含两个yield语句,这是怎么回事呢? 我知道生成器是如何工作的,但只有一个yield语句。 请帮忙解答,谢谢!

可能只有一个yield语句,但它不是在循环中吗? - Paul Rooney
有两个,我只是没有理解两个yield的基本原理。我以为一旦遇到yield就不会进入其他yield,但事实并非如此。 - unnobtainium
这个问题的规范版本有数百个链接,数百万次浏览,并且是Python标签中最受赞同的问题。问题中给出的第一个示例使用了两个yield语句,因此这不能成为反对的理由(即使逻辑应用也很简单)。令我感到困惑的是,在那个时候没有人注意到这样一个明显的重复关闭,更不用说让它闲置五年了。 - Karl Knechtel
3个回答

26
同样的方式,一个单独的yield起作用。在生成器中可以有任意多个yield,当调用它的__next__方法时,它会执行直到遇到下一个yield。然后你就可以得到返回的表达式,生成器暂停直到再次调用它的__next__方法。
运行一些next调用以查看这个过程:
>>> g = make_iterables_to_chain()  # get generator
>>> next(g) # start generator, go to first yield, get result
[1, 2, 3]
>>> next(g) # resume generator, go to second yield, get result
['a', 'b', 'c']
>>> # next(g) raises Exception since no more yields are found 

5

生成器实际上允许函数返回多次。每次执行yield语句时,该值将被返回给调用者,并且调用者可以继续执行函数。

通常,它们在for循环中用作可迭代对象。

以下函数会逐个元素地增加可迭代对象中的所有元素:

def inc_each(nums, inc):
    for i in nums:
        yield i + inc

以下是使用示例:
gen = inc_each([1, 2, 3, 4], 100)
print(list(gen)) # [101, 102, 103, 104]

list 在此处用于将任意可迭代对象(在本例中为生成器)转换为列表。

您所描述的函数执行了两个 yield 语句:

def make_iterables_to_chain():
    yield [1, 2, 3]
    yield ['a', 'b', 'c']

如果您调用它,它会返回一个生成器,如果迭代它,将会产生列表[1, 2, 3]['a', 'b', 'c']

gen = make_iterables_to_chain()
print(list(gen)) # [[1, 2, 3], ['a', 'b', 'c']]

itertools.chain.from_iterable 函数接受一个(可能是无限的)可迭代对象,将其“展平”,返回一个(可能是无限的)可迭代对象作为结果。

下面是一种实现方式:

def from_iterable(iterables):
    for iterable in iterables:
        for i in iterable:
            yield i

您的意思是第一次调用make_iterables_to_chain()函数时,它会返回第一个yield语句即[1,2,3],而第二次调用时则返回第二个yield语句即['a','b','c']? - unnobtainium
1
@ispeedster 生成器表达式实际上有一个额外的间接层。当你调用make_iterables_to_chain时,它返回一个生成器对象,可以使用iter()next()进行迭代(尽管这通常由for循环自动处理)。因此,函数本身只会被调用一次,但生成的对象可以被迭代,并且可以产生多个值。 - Challenger5

-1

在之前的答案基础上补充一点,当使用这样的代码时:

def my_yield_function():
  yield 1
  yield 'test'
  yield 2
  yield true

您可以使用以下代码解包所有值:
w,x,y,z = my_yield_function()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接