在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
生成器(例如生成器表达式返回的对象)是一个惰性迭代器。它只运行所需的代码,为您提供所需的值。如果不对生成器进行迭代,则不要求任何值,因此它不会运行任何代码,因此不会打印任何内容。
您可以通过调用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
这样迭代一个参数的函数),整个生成器就会被消耗掉,就像你多次调用next
一样。如果你一直手动调用next
,你最终会得到一个StopIteration
异常,这就是迭代器发出的信号表明它没有更多的值可以产生。
您尝试的列表理解没有相同的行为,因为与生成器不同,列表理解不是懒惰的。它立即运行所有代码,并根据它获得的所有值创建一个列表。你的列表理解将创建一个完整的None
值列表(因为这是print
返回的),并且所有数字将在列表被填充时打印。
请注意,您的代码的任何版本都不是Pythonic。您通常不应将生成器表达式或列表推导仅用于其副作用(例如print
ing值)。当您关心所产生的值(或放入列表中)时,请使用它们。如果你只想打印一系列数字,只需直接在范围内使用for
循环,并在循环体中调用print
:
for i in range(5):
print(i)
这是可接受的列表理解或生成器表达式,其代码具有副作用,只要计算的值也很有用。例如,如果您想通过不可靠的网络向一堆客户端发送消息,您可能会使用一些看起来像这样的代码:
results = [client.send(message) for client in clients] # client.send may return an error code
for result in results: # process the results after all the messages were sent
if result is not None: # no error means success
print("client.send error:", result) # report failures, but keep going
第一行的列表理解主要用于其副作用(发送消息),但返回值列表也很有用,因为它可以让我们看到发生了什么错误。
这是因为您正在创建一个生成器对象
(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网站。