【问题标题】:Python Loop Behavior with Generators带有生成器的 Python 循环行为
【发布时间】:2014-10-21 11:50:04
【问题描述】:

我很困惑为什么不同的循环结构在与简单的生成器一起使用时表现如此不同。考虑以下代码:

example_list = [1, 2, 3, 4, 5]
for_count = 0
next_count = 0
while_count = 0

def consumer(iterable):
    for item in iterable:
        yield    
    return

for item in consumer(example_list):
    print("For Count: {0}".format(for_count))
    for_count += 1

# First while loop
while consumer(example_list).next():
    print("Next Count: {0}".format(next_count))
    next_count += 1

# Second while loop
while consumer(example_list):
    print("While Count: {0}".format(while_count))
    while_count += 1
    if while_count > 10: # add contrived limit for infinite loop
        break

这段代码的输出是:

计数:0
计数:1
计数:2
计数:3
计数:4
而计数:0
而计数:1
而计数:2
而计数:3
而计数:4
计数:5
计数:6
计数:7
计数:8
计数:9
而计数:10

如果您能帮助理解以下内容,我将不胜感激:

  1. for 循环如何知道何时结束但第二个while 循环不知道?我预计两个 while 循环都会立即退出,因为产生了 None
  2. 为什么不使用.next() 引发异常? consumer 例程不是一个定义了__next__() 方法的类,那么当您使用yield 关键字时它会神奇地出现吗?
  3. 为什么如果我将consumer 更改为yield item,第一个while 循环会像第二个一样变为无限?
  4. 如果我将consumer 更改为简单地返回而不是产生任何东西,第二个while 循环将立即退出而不是变为无限。我一直认为yield 本质上是一个return,您可以从中恢复。为什么 while 循环会以不同方式处理它们?

【问题讨论】:

    标签: python loops return generator yield


    【解决方案1】:

    for 循环

    您的第一个 for 循环按预期工作。 更新Mark Ransom 注意到您的yield 没有伴随预期的item,因此它只返回[None, None, None, None, None] 而不是[1, 2, 3, 4, 5] - 但它仍然会遍历列表。

    第一个while 循环

    The very same commentator 还注意到第一个 while 循环永远不会启动,因为 0 在 Python 中是 False 等效项。

    第二个while 循环

    在第二个 while 循环中,您正在测试 consumer(example_list) 的值。这是生成器对象本身,而不是其next() 返回的值。对象本身永远不会等于 None 或任何其他 False 等价物 - 所以你的循环永远不会结束。

    这可以通过在循环中打印 consumer(example_list) 的值(即您的 while 条件)来看到:

    >>> while_count=0
    >>> while consumer(example_list):
    ...     print while_count, consumer(example_list)
    ...     while_count += 1
    ...     if while_count > 10:
    ...         break
    

    给予:

    0 <generator object consumer at 0x1044a1b90>
    1 <generator object consumer at 0x1044a1b90>
    2 <generator object consumer at 0x1044a1b90>
    3 <generator object consumer at 0x1044a1b90>
    4 <generator object consumer at 0x1044a1b90>
    5 <generator object consumer at 0x1044a1b90>
    6 <generator object consumer at 0x1044a1b90>
    7 <generator object consumer at 0x1044a1b90>
    8 <generator object consumer at 0x1044a1b90>
    9 <generator object consumer at 0x1044a1b90>
    10 <generator object consumer at 0x1044a1b90>
    

    第二项是对象,它从不等于None

    【讨论】:

    • 中间的例子第一次返回0确实评估为False,因此立即终止循环。
    • @MarkRansom 真的,好点。没有事件查看那个循环。
    • 其实我弄错了,因为yield之后什么都没有,它返回None而不是0,它也计算为False。所以我是对的,但出于错误的原因。
    • @MarkRansom - 太棒了。顺便说一句,使用断点和调试器会很清楚。
    【解决方案2】:

    只回答您问题的一部分:

    您对while 循环的误解是,while 循环不会像for item in generator 循环那样自行迭代生成器对象。

    第二个 while 循环中的 consumer(example_list) 总是返回一个生成器,它的计算结果为布尔值 True,因此循环永远运行。

    在第一个 while 循环中,您正在测试来自该生成器的第一个收益值,即 None。因此,循环甚至没有开始。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-20
      • 1970-01-01
      • 1970-01-01
      • 2023-02-06
      • 2013-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多