python 中的 'yield' 关键字是如何真正起作用的,尤其是当它带有递归时?
How does the 'yield' keyword in python really work, especially when it comes with recursion?
我正在使用 python 来展平嵌套列表,例如 [1,2,[3,4,[5,[[6,7]]]]]
,我想创建一个生成器,这样我就可以使用 for 循环来逐个打印嵌套列表中的所有数字列表。但它并没有像我预期的那样工作。
当我用 'print' 替换 'yield' 关键字时,数字会一个接一个地打印出来。但是这样它就不再是发电机了。
以下无法正常工作:
from collections.abc import Iterable
def flatten(nested):
for item in nested:
if isinstance(item, Iterable):
flatten(item)
else:
yield item
x = [1,2,[3,4,[5,[[6,7]]]]]
y = flatten(x)
for element in y:
print(element)
但是,如果我编写如下代码,我将 yield
替换为
print
,数字将正确打印
from collections.abc import Iterable
def flatten(nested):
for item in nested:
if isinstance(item, Iterable):
flatten(item)
else:
print(item)
x = [1,2,[3,4,[5,[[6,7]]]]]
y = flatten(x)
加上'yield',如果我写:
x = [[1,2],[3,4,[5,[[6,7]]]]]
y = flatten(x)
y.__next__()
错误消息将是y.__next__() StopIteration
您永远不会 return 从递归调用中退出或退出。添加一个 yield from 它应该可以工作。
from collections.abc import Iterable
def flatten(nested):
for item in nested:
if isinstance(item, Iterable):
yield from flatten(item) #change here.
else:
yield item
x = [1,2,[3,4,[5,[[6,7]]]]]
y = flatten(x)
for element in y:
print(element)
#Output:
1
2
3
4
5
6
7
请注意,无论您使用 yield 还是 return,此问题也固有地存在于您的原始函数中。这就是为什么在递归调用中只使用 print
而没有使用 return 时应该小心,它可以掩盖这样一个事实,即虽然代码运行正常,但输出不正确 captured/used。
这修复了您的代码:
from collections.abc import Iterable
def flatten(nested):
for item in nested:
if isinstance(item, Iterable):
yield from flatten(item)
else:
yield item
x = [1, 2, [3, 4, [5, [[6, 7]]]]]
y = flatten(x)
for element in y:
print(element)
之所以有效,是因为您再次调用了 flatten
,但忘记了 yield from
(毕竟,新调用也是 returns 生成器)
请注意,isinstance(item, Iterable)
可能不是您想要的,因为它会中断字符串。字符串是 Iterable
,但在 for
循环中,从中返回的字符本身就是字符串。最好检查它是否是一个列表。
def flatten(nested):
for item in nested:
if isinstance(item, list):
yield from flatten(item)
else:
yield item
for element in flatten([1, 2, ['three', 4, [5, [[6, 7]]]]]):
print(element)
我正在使用 python 来展平嵌套列表,例如 [1,2,[3,4,[5,[[6,7]]]]]
,我想创建一个生成器,这样我就可以使用 for 循环来逐个打印嵌套列表中的所有数字列表。但它并没有像我预期的那样工作。
当我用 'print' 替换 'yield' 关键字时,数字会一个接一个地打印出来。但是这样它就不再是发电机了。
以下无法正常工作:
from collections.abc import Iterable
def flatten(nested):
for item in nested:
if isinstance(item, Iterable):
flatten(item)
else:
yield item
x = [1,2,[3,4,[5,[[6,7]]]]]
y = flatten(x)
for element in y:
print(element)
但是,如果我编写如下代码,我将 yield
替换为
print
,数字将正确打印
from collections.abc import Iterable
def flatten(nested):
for item in nested:
if isinstance(item, Iterable):
flatten(item)
else:
print(item)
x = [1,2,[3,4,[5,[[6,7]]]]]
y = flatten(x)
加上'yield',如果我写:
x = [[1,2],[3,4,[5,[[6,7]]]]]
y = flatten(x)
y.__next__()
错误消息将是y.__next__() StopIteration
您永远不会 return 从递归调用中退出或退出。添加一个 yield from 它应该可以工作。
from collections.abc import Iterable
def flatten(nested):
for item in nested:
if isinstance(item, Iterable):
yield from flatten(item) #change here.
else:
yield item
x = [1,2,[3,4,[5,[[6,7]]]]]
y = flatten(x)
for element in y:
print(element)
#Output:
1
2
3
4
5
6
7
请注意,无论您使用 yield 还是 return,此问题也固有地存在于您的原始函数中。这就是为什么在递归调用中只使用 print
而没有使用 return 时应该小心,它可以掩盖这样一个事实,即虽然代码运行正常,但输出不正确 captured/used。
这修复了您的代码:
from collections.abc import Iterable
def flatten(nested):
for item in nested:
if isinstance(item, Iterable):
yield from flatten(item)
else:
yield item
x = [1, 2, [3, 4, [5, [[6, 7]]]]]
y = flatten(x)
for element in y:
print(element)
之所以有效,是因为您再次调用了 flatten
,但忘记了 yield from
(毕竟,新调用也是 returns 生成器)
请注意,isinstance(item, Iterable)
可能不是您想要的,因为它会中断字符串。字符串是 Iterable
,但在 for
循环中,从中返回的字符本身就是字符串。最好检查它是否是一个列表。
def flatten(nested):
for item in nested:
if isinstance(item, list):
yield from flatten(item)
else:
yield item
for element in flatten([1, 2, ['three', 4, [5, [[6, 7]]]]]):
print(element)