【问题标题】:How does the 'yield' keyword in python really work, especially when it comes with recursion?python 中的“yield”关键字是如何真正起作用的,尤其是当它带有递归时?
【发布时间】:2019-12-07 11:40:30
【问题描述】:

我正在使用 python 来展平嵌套列表,例如[1,2,[3,4,[5,[[6,7]]]]],我想创建一个生成器,以便我可以使用 for 循环在嵌套列表中一一打印所有数字。但它并没有像我预期的那样工作。

当我将 'yield' 关键字替换为 'print' 时,数字会一一打印出来。但是这样一来,它就不再是生成器了。

以下无法正常工作:

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

【问题讨论】:

    标签: python recursion generator yield iterable


    【解决方案1】:

    这会修复您的代码:

    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(毕竟新调用也返回了一个生成器)

    请注意,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中的一个棘手点:字符是刺!因此,如果我在列表中包含一个字符串,它会将其展平为字符,但是字符仍然是刺痛的,因此它会不断变平......那是一场灾难。感谢您注意到我!
    【解决方案2】:

    您永远不会从递归调用中返回或让步。添加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 并且在递归调用中没有返回时应该小心的原因,它可以掩盖这样一个事实,即虽然代码运行正常,但输出没有被正确捕获/使用。

    【讨论】:

      猜你喜欢
      • 2016-08-30
      • 1970-01-01
      • 2019-07-05
      • 2011-09-09
      • 2015-05-25
      • 2017-03-19
      • 1970-01-01
      • 2011-01-17
      • 1970-01-01
      相关资源
      最近更新 更多