在python3中,为什么以下代码会打印数字0,1,2,3,4?
[print(i) for i in range(5)]
但是如果你用生成器做同样的事情,它什么都不做:
(print(i) for i in range(5))
使用生成器获得相同结果的唯一方法是像这样迭代它:
z = (print(i) for i in range(5))
for i in z:
i
答案 0 :(得分:2)
这是因为您正在使用
创建生成器对象(print(i) for i in range(5))
不是清单。如果将其更改为
list(print(i) for i in range(5))
它的行为与你的第一个陈述相同。
生成器是特殊类型的迭代器。有关更多信息,请查看Python文档here。
(print(i) for i in range(5))
中括号之间的代码告诉生成器一旦迭代它就如何“动作”它将不会实际调用打印函数直到迭代结束。由于列表理解,列表调用print函数。使用[print(i) for i in range(5)]
或list(print(i) for i in range(5))
相当于
for i in range(5):
print i
由于列表理解。所以你实际上是在调用print函数。有关列表理解的更多信息,请查看this网站。
答案 1 :(得分:2)
生成器,例如生成器表达式返回的对象是一个惰性迭代器。它只运行所需的代码,为您提供所需的值。如果你不迭代生成器,你不要求任何值,因此它不会运行任何代码,因此不会打印任何内容。
您可以通过调用它上的next
来请求迭代器中的各个值:
gen = (print(i) for i in range(5))
x = next(gen) # causes 0 to be printed
y = next(gen) # causes 1 to be printed
第一次调用next
会询问生成器的第一个值,因此会使用print
中的第一个值调用range
。第二次调用next
将导致range
的第二个值被打印。 x
和y
都将被分配None
,因为这是print
返回的内容,因此生成器会产生什么。
如果将生成器置于for
循环中(或将其传递给迭代参数的list
函数),整个生成器将被消耗,就像你调用{{} 1}}在它上面多次。如果你一直手动调用next
,你最终会得到一个next
异常,这就是迭代器发出的信号表明它没有更多的值可以产生。
您尝试的列表理解没有相同的行为,因为与生成器不同,列表理解并不是懒惰的。它立即运行所有代码,并根据它获得的所有值创建一个列表。您的列表理解将创建一个包含StopIteration
值的列表(因为这是None
返回的值),并且所有数字将在列表填充时打印。
请注意,您的代码版本都不是Pythonic。您通常不应将生成器表达式或列表推导仅用于其副作用(例如print
值)。当您关心所产生的值(或放入列表中)时,请使用它们。如果您只想打印一系列数字,只需直接在范围内使用print
循环,然后在循环体中调用for
:
print
只要正在计算的值也很有用,那么带有副作用的代码是可接受的列表推导或生成器表达式。例如,如果您想通过不可靠的网络向一堆客户端发送消息,您可能会使用一些看起来像这样的代码:
for i in range(5):
print(i)
第一行的列表理解主要用于副作用(发送消息),但返回值列表也很有用,因为它可以让我们看到发生了什么错误。