Python: друкувати вираз генератора?


103

У оболонці 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

Окрім цього (чи написання функції помічника), чи можу я легко оцінити та надрукувати цей генераторний об’єкт у інтерактивній оболонці?


2
У чому справжня проблема? Чого вам не вистачає?
Андреас Юнг

3
@pynator: "Справжня проблема" полягає лише в тому, що я хочу мати можливість надрукувати вміст, generator objectколи я інтерактивно будую розуміння за інтерактивним запитом. Виклик list(_)робить це. Що я робив - це розуміння списку, а потім перетворюю їх у genexp у більшому коді. Вони можуть провалюватися під час виконання так, як це не дає розуміння списку.
вовк

5
Коротка відповідь полягає в тому, що вираз генератора не можна надрукувати, оскільки його значень не існує; вони генеруються на вимогу. Що ви можете зробити (якщо припустити, що генератор колись зупиняється) - це отримати всі значення з нього, як, наприклад list(), і потім надрукувати їх.
Кос

Відповіді:


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

Так, це перетворить вираз генератора в розуміння списку. Це те саме, що і список викликів () на ньому. Тож спосіб внести генераторний вираз до списку - поставити навколо нього дужки.

Детальне пояснення:

Генераторний вираз - це "голий" 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

2
list( generator-expression )не друкує вираз генератора; він генерує список (а потім друкує його в інтерактивній оболонці). Замість того, щоб генерувати список, у Python 3 ви можете занести вираз генератора в оператор друку. Тобто) print(*(generator-expression)). Це друкує елементи без коми та без дужок на початку та в кінці.
AJNeufeld

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)).
Мілан Велебіт

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.