Python: imprimir uma expressão do gerador?

na shell Python, Se eu introduzir uma compreensão de lista como:

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

tenho um resultado bem impresso:

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

o mesmo para uma compreensão de dicionário:

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

Se eu entrar numa expressão do gerador, não recebo uma resposta tão amigável:

>>> (x for x in string.letters if x in (y for y in "BigMan on campus"))
<generator object <genexpr> at 0x1004a0be0>
Eu sei que consigo fazer isto.
>>> for i in _: print i,
a c g i m n o p s u B M
Para além disso (ou escrevendo uma função auxiliar), posso avaliar e imprimir facilmente o objecto gerador na shell interactiva?

 74
Author: the wolf, 2011-03-02

5 answers

Resposta Rápida:

Fazer list() em torno de uma expressão do gerador é (quase) exatamente equivalente a ter [] suportes em torno dele. Por isso, sim, podes fazê-lo.

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

Mas também podes fazer

>>> [x for x in string.letters if x in (y for y in "BigMan on campus")]
Sim, isso vai transformar a expressão do gerador numa compreensão da lista. É a mesma coisa e lista de chamadas() nele. Então a maneira de fazer uma expressão do gerador em uma lista é colocar parêntesis em torno dela.

Detalhado anotações:

Uma expressão do gerador é uma expressão" nua " for. Assim:

x*x for x in range(10)
Não podes pôr isso numa linha sozinha, vais ter um erro de sintaxe. Mas você pode colocar parêntesis em torno dele.
>>> (x*x for x in range(10))
<generator object <genexpr> at 0xb7485464>
Isto às vezes é chamado de compreensão do gerador, embora eu ache que o nome oficial ainda é expressão do gerador, não há realmente nenhuma diferença, os parêntesis só estão lá para tornar a sintaxe válida. Você não precisa deles se você está passando 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]

Basicamente todas as outras compreensões disponíveis em Python 3 e Python 2.7 são apenas açúcar sintático em torno de uma expressão do gerador. Definir as compreensões:

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

As compreensões 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}

E listar as compreensões em 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]

No Python 2, as compreensões de listas não são apenas açúcar sintático. Mas a única diferença é que o X vai sob o Python 2 vazar para o espaco.

>>> x
9
Enquanto sob o Python 3 você vai ter
>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

Isto significa que a melhor maneira de obter uma boa impressão do conteúdo da sua expressão gerador em Python é fazer com que a lista compreenda! No entanto, isso obviamente não vai funcionar se você já tem um objeto gerador. Fazer isso só fará uma lista de um gerador:

>>> foo = (x*x for x in range(10))
>>> [foo]
[<generator object <genexpr> at 0xb7559504>]
Nesse caso, terá de ligar.list():
>>> list(foo)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Embora isto funcione, mas é um pouco ... estúpido.
>>> [x for x in foo]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
 116
Author: Lennart Regebro, 2011-03-02 08:48:57

Você pode apenas embrulhar a expressão numa chamada para 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
Author: Björn Pollex, 2017-08-30 21:08:07

Ao contrário de uma lista ou de um dicionário, um gerador pode ser infinito. Fazer isto não funcionaria.

def gen():
    x = 0
    while True:
        yield x
        x += 1
g1 = gen()
list(g1)   # never ends
Além disso, ler um gerador altera-o, por isso não há uma maneira perfeita de o ver. Para ver uma amostra da saída do gerador, você poderia fazer
g1 = gen()
[g1.next() for i in range(10)]
 10
Author: chad, 2013-05-26 19:21:50

Ou você pode sempre map sobre um iterador, sem a necessidade de construir uma lista intermédia:

>>> _ = map(sys.stdout.write, (x for x in string.letters if x in (y for y in "BigMan on campus")))
acgimnopsuBM
 7
Author: lbolla, 2011-11-24 19:06:11
>>> 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']
 2
Author: Andreas Jung, 2011-03-02 07:44:17