如何打印生成器表达式?
在Python的命令行中,如果我输入一个列表推导式,比如:
>>> [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
除了这个方法(或者写一个辅助函数),在交互式命令行中,我能否轻松地评估并打印这个生成器对象呢?
8 个回答
跟列表或者字典不一样,生成器可以是无限的。这样做是行不通的:
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)]
或者你可以直接对一个迭代器使用 map
,这样就不需要先创建一个中间的列表了:
>>> _ = map(sys.stdout.write, (x for x in string.letters if x in (y for y in "BigMan on campus")))
acgimnopsuBM
简短回答:
在生成器表达式外面加上 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 中,列表推导式不仅仅是语法糖。唯一的区别是,在 Python 2 中,x 会泄露到命名空间中。
>>> 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]