使用python3中的列表推导和生成器打印

时间:2017-12-22 04:04:24

标签: python-3.x generator list-comprehension

在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

2 个答案:

答案 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的第二个值被打印。 xy都将被分配None,因为这是print返回的内容,因此生成器会产生什么。

如果将生成器置于for循环中(或将其传递给迭代参数的list函数),整个生成器将被消耗,就像你调用{{} 1}}在它上面多次。如果你一直手动调用next,你最终会得到一个next异常,这就是迭代器发出的信号表明它没有更多的值可以产生。

您尝试的列表理解没有相同的行为,因为与生成器不同,列表理解并不是懒惰的。它立即运行所有代码,并根据它获得的所有值创建一个列表。您的列表理解将创建一个包含StopIteration值的列表(因为这是None返回的值),并且所有数字将在列表填充时打印。

请注意,您的代码版本都不是Pythonic。您通常不应将生成器表达式或列表推导仅用于其副作用(例如print值)。当您关心所产生的值(或放入列表中)时,请使用它们。如果您只想打印一系列数字,只需直接在范围内使用print循环,然后在循环体中调用for

print

只要正在计算的值也很有用,那么带有副作用的代码是可接受的列表推导或生成器表达式。例如,如果您想通过不可靠的网络向一堆客户端发送消息,您可能会使用一些看起来像这样的代码:

for i in range(5):
    print(i)

第一行的列表理解主要用于副作用(发送消息),但返回值列表也很有用,因为它可以让我们看到发生了什么错误。