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