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

问题描述 投票:0回答:2

在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
python-3.x generator list-comprehension
2个回答
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这样迭代一个参数的函数),整个生成器就会被消耗掉,就像你多次调用next一样。如果你一直手动调用next,你最终会得到一个StopIteration异常,这就是迭代器发出的信号表明它没有更多的值可以产生。

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

请注意,您的代码的任何版本都不是Pythonic。您通常不应将生成器表达式或列表推导仅用于其副作用(例如printing值)。当您关心所产生的值(或放入列表中)时,请使用它们。如果你只想打印一系列数字,只需直接在范围内使用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

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


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网站。

© www.soinside.com 2019 - 2024. All rights reserved.