列表理解中生成器的过早结束

bbm*_*rva 9 python list-comprehension generator

我在列表推导中使用生成器,并且在其中一个生成器提前结束时获得了一些意外行为.为什么在列表推导之外创建生成器会导致行为发生变化?

我创建的生成器如下:

def inc_range(a,b):
    for i in range(min(a,b), max(a,b) + 1):
        yield i
Run Code Online (Sandbox Code Playgroud)

第一种呼叫方式如下:

[(i,j) for i in inc_range(1,3) for j in inc_range(4,6)]
Run Code Online (Sandbox Code Playgroud)

这给了我以下结果:

[(1, 4), (1, 5), (1, 6), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6)]
Run Code Online (Sandbox Code Playgroud)

调用它的第二种方式如下:

a = inc_range(1,3)
b = inc_range(4,6)

[(i,j) for i in a for j in b]
Run Code Online (Sandbox Code Playgroud)

这给了我以下内容:

[(1, 4), (1, 5), (1, 6)]
Run Code Online (Sandbox Code Playgroud)

试验一下,以下两个例子给了我第一个结果:

a = range(1,4)
b = range(4,7)

[(i,j) for i in a for j in b]
Run Code Online (Sandbox Code Playgroud)
a = (i for i in range(1,4))
b = (i for i in range(4,7))

a = list(a)
b = list(b)

[(i,j) for i in a for j in b]
Run Code Online (Sandbox Code Playgroud)

虽然以下给了我第二个结果.

a = (i for i in range(1,4))
b = (i for i in range(4,7))

[(i,j) for i in a for j in b]
Run Code Online (Sandbox Code Playgroud)

关于发电机我在这里违反了什么规则?为什么在将生成器分配给变量之前将它们分配给变量,而不是直接使用它们?

解答

查看以下答案,这些答案帮助我了解这里发生的事情:

Alex Yu mkrieger1

mkr*_*er1 6

为了获得所需的结果,"内部"发生器必须运行多次,因为"外部"发生器产生一个值.

但是,在第一次运行后,"内部"发电机耗尽,无法再次运行.

添加一个print说明这个(简化示例):

>>> def inc(a, b):
...    for i in range(a, b):
...        print(i)
...        yield i
...
>>> a = inc(1, 4)
>>> b = inc(4, 7)
>>> [(i, j) for i in a for j in b]
1  # <-- a begins to run
4  # <-- b begins to run
5
6  # <-- b exhausted here
2  # <-- a continued, but not resulting in list item, because lacking value from b
3
[(1, 4), (1, 5), (1, 6)]
Run Code Online (Sandbox Code Playgroud)

不将变量存储在变量中的原因是因为为"外部"生成器的每次迭代创建了一个新的"内部"生成器.再次,由一些印刷品说明:

>>> def inc(a, b):
...    print('started', a, b)
...    for i in range(a, b):
...        yield i
... 
>>> [(i, j) for i in inc(1, 4) for j in inc(4, 7)]
started 1 4
started 4 7
started 4 7
started 4 7
[(1, 4), (1, 5), (1, 6), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6)]
Run Code Online (Sandbox Code Playgroud)

使用range对象或列表按预期工作的原因是因为它们可以任意迭代多次而不会耗尽.