在 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 中评估和打印该生成器对象吗?
快速回答:
在生成器表达式周围执行
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() 是同样的事情。 所以将生成器表达式放入列表的方法就是用括号括起来。
在 Python 3 中,您可以将生成器表达式放入 print 语句中:
>>> print(*(x for x in string.ascii_letters if x in (y for y in "BigMan on campus")))
a c g i m n o p s u B M
详细说明:
生成器表达式是一个“裸”
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]
或者您始终可以通过迭代器
map
,而不需要构建中间列表:
>>> _ = map(sys.stdout.write, (x for x in string.letters if x in (y for y in "BigMan on campus")))
acgimnopsuBM
与列表或字典不同,生成器可以是无限的。这样做是行不通的:
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)]
您可以将表达式包装在对
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']
Generator 对象不存储实际数据,它基本上只是一个表达式。程序无法在不求值的情况下打印表达式的值。生成器对象(生成器表达式)可以通过类型转换为任何可迭代数据类型来求值。
例如。
list(genexpr)
dict(genexpr)
set(genexpr)
for data in genexpr:
补充
生成生成器表达式然后进行类型转换比直接创建所需的数据类型对象慢 20%。因此,如果我们需要完整数据,最好使用
data=[x for x in range(0,10)]
比使用
genexpr=(x for x in range(0,10))
data=list(genexpr)
你也可以这样做:
gen = (i for i in 'abcde')
print( *gen ) # => a b c d e
>>> 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']
print(i for i in range(9))
如果我们运行它,我们将得到输出: -
打印生成器的一种简单方法是将其转换为列表。 如果我们将代码修改为
print(*[i for i in range(9)])
因此我们将得到输出: 0 1 2 3 4 5 6 7 8