Python: напечатать выражение генератора? - PullRequest
85 голосов
/ 02 марта 2011

В оболочке 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

Кроме этого (или написания вспомогательной функции), я могу легко оценить и напечатать этот объект генератора в интерактивной оболочке?

Ответы [ 5 ]

137 голосов
/ 02 марта 2011

Быстрый ответ:

Выполнение 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")]

Да, это превратит выражение генератора в понимание списка. Это то же самое и вызов списка () на нем. Таким образом, способ создать выражение генератора в списке - заключить его в квадратные скобки.

Подробное объяснение:

Генераторное выражение - это «голое» 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:

>>> 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]
15 голосов
/ 02 марта 2011

Вы можете просто обернуть выражение в вызове в 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']
13 голосов
/ 26 мая 2013

В отличие от списка или словаря, генератор может быть бесконечным. Это не сработает:

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)]
10 голосов
/ 24 ноября 2011

Или вы всегда можете map через итератор, без необходимости создавать промежуточный список:

>>> _ = map(sys.stdout.write, (x for x in string.letters if x in (y for y in "BigMan on campus")))
acgimnopsuBM
2 голосов
/ 02 марта 2011
>>> 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']
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...