Python:打印生成器表达式?


103

在Python Shell中,如果我输入列表理解,例如:

>>> [x for x in string.letters if x in [y for y in "BigMan on campus"]]

我得到一个很好的打印结果:

['a', 'c', 'g', 'i', 'm', 'n', 'o', 'p', 's', 'u', 'B', 'M']

对于字典理解也是如此:

>>> {x:x*2 for x in range(1,10)}
{1: 2, 2: 4, 3: 6, 4: 8, 5: 10, 6: 12, 7: 14, 8: 16, 9: 18}

如果输入生成器表达式,则不会得到如此友好的响应:

>>> (x for x in string.letters if x in (y for y in "BigMan on campus"))
<generator object <genexpr> at 0x1004a0be0>

我知道我可以这样做:

>>> for i in _: print i,
a c g i m n o p s u B M

除此之外(或者编写辅助函数),我可以轻松地在交互式shell中评估并打印该生成器对象吗?


2
这里真正的问题是什么?你想念什么?
Andreas Jung

3
@pynator:“真正的问题”只是我希望能够generator object在交互式提示下以交互式方式构建理解时能够打印内容。调用即可list(_)。我所做的是使用列表推导,然后在较大的代码中将其转换为genexp。这些可能会在运行时以列表理解所不具备的方式失败。

5
简短的答案是生成器表达式无法打印,因为其值不存在。它们是按需生成的。您可以做的(假设生成器在某个时间停止)是从中获取所有值,例如使用list(),然后打印它们。
科斯,

Answers:


161

快速回答:

list()围绕发电机表达是(几乎)完全等同于具有 []在其周围括号。是的,你可以

>>> list((x for x in string.letters if x in (y for y in "BigMan on campus")))

但是你也可以做

>>> [x for x in string.letters if x in (y for y in "BigMan on campus")]

是的,这会将生成器表达式转换为列表理解。这是同一件事,并在其上调用list()。因此,将生成器表达式放入列表的方法是在方括号中加上括号。

详细说明:

生成器表达式是“裸” for表达式。像这样:

x*x for x in range(10)

现在,您不能将其本身放在一行上,而会遇到语法错误。但是您可以在其周围加上括号。

>>> (x*x for x in range(10))
<generator object <genexpr> at 0xb7485464>

有时这被称为生成器理解,尽管我认为正式名称仍然是生成器表达式,但实际上并没有什么区别,括号只是为了使语法有效。如果要将其作为唯一参数传递给函数,则不需要它们,例如:

>>> sorted(x*x for x in range(10))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

基本上,Python 3和Python 2.7中可用的所有其他理解只是围绕生成器表达式的语法糖。设定理解:

>>> {x*x for x in range(10)}
{0, 1, 4, 81, 64, 9, 16, 49, 25, 36}

>>> set(x*x for x in range(10))
{0, 1, 4, 81, 64, 9, 16, 49, 25, 36}

词典理解:

>>> dict((x, x*x) for x in range(10))
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

>>> {x: x*x for x in range(10)}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

并列出Python 3下的理解:

>>> list(x*x for x in range(10))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

>>> [x*x for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

在Python 2下,列表理解不仅是语法糖。但是唯一的区别是x在Python 2下会泄漏到名称空间中。

>>> x
9

在Python 3下,您会得到

>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

这意味着,在Python中很好地打印生成器表达式的内容的最佳方法是对列表进行理解!但是,如果您已经具有生成器对象,那么这显然将行不通。这样做只会列出一个生成器的列表:

>>> foo = (x*x for x in range(10))
>>> [foo]
[<generator object <genexpr> at 0xb7559504>]

在这种情况下,您需要致电list()

>>> list(foo)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

虽然这有效,但是有点愚蠢:

>>> [x for x in foo]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

5
官方用语仍然是“生成器表达”,因为“理解”一词意味着迭代,这是genexp 不会做的一件事,因为这个问题和答案很好地说明了这一点:)
ncoghlan 2011年

2
list( generator-expression )没有打印生成器表达式;它正在生成一个列表(然后将其打印在交互式shell中)。在Python 3中,除了生成列表以外,您还可以将生成器表达式吐到print语句中。即)print(*(generator-expression))。这将在元素的开头和结尾打印没有逗号的元素。
AJNeufeld '18年

17

与列表或字典不同,生成器可以是无限的。这样做是行不通的:

def gen():
    x = 0
    while True:
        yield x
        x += 1
g1 = gen()
list(g1)   # never ends

另外,读取生成器也会对其进行更改,因此没有一种完美的方式来查看它。要查看发电机输出的样本,您可以

g1 = gen()
[g1.next() for i in range(10)]

2
由于声明生成器可以是无限的,所以被投票,从而导致循环或完全停止(取决于您的规格(lol))。
米兰Velebit

16

您可以将表达式包装在对的调用中list

>>> list(x for x in string.letters if x in (y for y in "BigMan on campus"))
['a', 'c', 'g', 'i', 'm', 'n', 'o', 'p', 's', 'u', 'B', 'M']

15

或者,您始终可以map遍历迭代器,而无需构建中间列表:

>>> _ = map(sys.stdout.write, (x for x in string.letters if x in (y for y in "BigMan on campus")))
acgimnopsuBM

3
这是唯一实际打印生成器内容而不创建大对象的答案。
Marek R

2
>>> list(x for x in string.letters if x in (y for y in "BigMan on campus"))
['a', 'c', 'g', 'i', 'm', 'n', 'o', 'p', 's', 'u', 'B', 'M']

如果生成器是无限的,则会导致循环。
米兰Velebit
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.