Python:打印生成器表达式?

问题:Python:打印生成器表达式?

在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中评估并打印该生成器对象吗?

In the Python shell, if I enter a list comprehension such as:

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

I get a nicely printed result:

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

Same for a dictionary comprehension:

>>> {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}

If I enter a generator expression, I get not such a friendly response:

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

I know I can do this:

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

Other than that (or writing a helper function) can I easily evaluate and print that generator object in the interactive shell?


回答 0

快速回答:

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]

Quick answer:

Doing list() around a generator expression is (almost) exactly equivalent to having [] brackets around it. So yeah, you can do

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

But you can just as well do

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

Yes, that will turn the generator expression into a list comprehension. It’s the same thing and calling list() on it. So the way to make a generator expression into a list is to put brackets around it.

Detailed explanation:

A generator expression is a “naked” for expression. Like so:

x*x for x in range(10)

Now, you can’t stick that on a line by itself, you’ll get a syntax error. But you can put parenthesis around it.

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

This is sometimes called a generator comprehension, although I think the official name still is generator expression, there isn’t really any difference, the parenthesis are only there to make the syntax valid. You do not need them if you are passing it in as the only parameter to a function for example:

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

Basically all the other comprehensions available in Python 3 and Python 2.7 is just syntactic sugar around a generator expression. Set comprehensions:

>>> {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 comprehensions:

>>> 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}

And list comprehensions under 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]

Under Python 2, list comprehensions is not just syntactic sugar. But the only difference is that x will under Python 2 leak into the namespace.

>>> x
9

While under Python 3 you’ll get

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

This means that the best way to get a nice printout of the content of your generator expression in Python is to make a list comprehension out of it! However, this will obviously not work if you already have a generator object. Doing that will just make a list of one generator:

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

In that case you will need to call list():

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

Although this works, but is kinda stupid:

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

回答 1

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

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)]

Unlike a list or a dictionary, a generator can be infinite. Doing this wouldn’t work:

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

Also, reading a generator changes it, so there’s not a perfect way to view it. To see a sample of the generator’s output, you could do

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

回答 2

您可以将表达式包装在对的调用中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']

You can just wrap the expression in a call to 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']

回答 3

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

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

Or you can always map over an iterator, without the need to build an intermediate list:

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

回答 4

>>> 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']
>>> 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']