当使用递归时,Python中的“yield”会上升到调用堆栈吗?

2024-09-22 20:36:20 发布

您现在位置:Python中文网/ 问答频道 /正文

在学习Bill Lubanovic介绍Python的过程中,我修改了一些代码以理解第9章中的flatte()函数

def flatten(lol):
    for item in lol:
        if isinstance(item, list):
            for subitem in flatten(item):
                print('yield1 ', subitem)
                yield subitem
        else:
            print('yield2 ', item)
            yield item

lol = [1, 2, [3, 4, 5], [6, [7, 8, 9],[]]]
list(flatten(lol))

我期望的输出是

('yield2 ', 1)
('yield2 ', 2)
('yield2 ', 3)
('yield2 ', 4)
('yield2 ', 5)
('yield1 ', 3)
('yield1 ', 4)
('yield1 ', 5)
...
...(skipped)

但程序的正确输出如下所示:

('yield2 ', 1)
('yield2 ', 2)
('yield2 ', 3)
('yield1 ', 3)
('yield2 ', 4)
('yield1 ', 4)
('yield2 ', 5)
('yield1 ', 5)
...
...(skipped)

我不明白为什么“('yield1',3)”在('yield2',4)之前被打印出来,即使调用的内部flatte()中的循环还没有结束。 我想知道递归时“yield”是否会展开堆栈


Tags: inforitemlistprintyieldlolbill
2条回答

当您迭代for subitem in flatten(item)时,调用返回到flatten,它首先打印项目yield2 value,然后您在循环中使用yield1 value再次打印它,这就是为什么,它会被打印两次,一次用于yield1,一次用于yield2,并且它不会像您在问题标题中提到的那样向上堆叠

另一方面,建议使用yield from ...对生成器进行递归调用,而不是手动迭代生成器调用

def flatten(lol):
    for item in lol:
        if isinstance(item, list):
            yield from flatten(item)
            # for subitem in flatten(item):
            #    print('yield1 ', subitem)
            #     yield subitem
        else:
            print('yield2 ', item)
            yield item

lol = [1, 2, [3, 4, 5], [6, [7, 8, 9], []]]
list(flatten(lol))

输出:

yield2  1
yield2  2
yield2  3
yield2  4
yield2  5
yield2  6
yield2  7
yield2  8
yield2  9

[1, 2, 3, 4, 5, 6, 7, 8, 9]

是的,yield语句暂停当前函数的执行,并将程序的控制权交给调用代码,其中生成的项将是正在迭代的生成器的下一个值。只有当从生成器请求进一步的值时,函数的内部调用才会恢复

如果您尝试较小的输入,并尝试使用next()手动迭代,您可能会更好地理解:

>>> gen = flatten([[1, [2]], 3])

>>> print("output", next(gen))
yield2  1
yield1  1
output 1

>>> print("output", next(gen))
yield2  2
yield1  2
yield1  2
output 2

>>> print("output", next(gen))
yield2  3
output 3

>>> print("output", next(gen))
Traceback (most recent call last):

  File "<ipython-input-16-1c675fe35f03>", line 1, in <module>
    print("output", next(gen))

StopIteration

相关问题 更多 >