Aderir a uma lista:
>>> ''.join([ str(_) for _ in xrange(10) ])
'0123456789'
join
deve ter um iterável.
Aparentemente, join
o argumento de é [ str(_) for _ in xrange(10) ]
, e é uma compreensão de lista .
Veja isso:
>>>''.join( str(_) for _ in xrange(10) )
'0123456789'
Agora, join
o argumento de é apenas str(_) for _ in xrange(10)
, não []
, mas o resultado é o mesmo.
Por quê? Será str(_) for _ in xrange(10)
também produzir uma lista ou um iterable?
python
list-comprehension
Alcott
fonte
fonte
join
provavelmente está escrito em C e, portanto, funciona muito mais rápido do que uma compreensão de lista ... Tempo de teste!_
não tem nenhum significado especial, é um nome de variável regular. Geralmente é usado como um nome descartável, mas não é o caso (você está usando a variável). Eu evitaria usá-lo em um código (pelo menos dessa forma).Respostas:
>>>''.join( str(_) for _ in xrange(10) )
Isso é chamado de expressão geradora e é explicado no PEP 289 .
A principal diferença entre expressões geradoras e compreensões de lista é que as primeiras não criam a lista na memória.
Observe que há uma terceira maneira de escrever a expressão:
''.join(map(str, xrange(10)))
fonte
( str(_) for _ in xrange(10) )
. Mas eu estava confuso, porque o()
pode ser omitido emjoin
, o que significa que o código deveria ser como `'' .join ((str (_) for _ in xrange (10))), certo?tup = 1, 2, 3; print(tup)
. Com isso em mente, usarfor
como parte de uma expressão cria o gerador e os parênteses estão lá apenas para distingui-lo de um loop escrito incorretamente.Os outros entrevistados estavam corretos ao responder que você havia descoberto uma expressão geradora (que tem uma notação semelhante às compreensões de lista, mas sem os colchetes ao redor).
Em geral, genexps (como são carinhosamente conhecidos) são mais eficientes em termos de memória e mais rápidas do que as compreensões de listas.
NO ENTANTO, no caso de
''.join()
, a compreensão de uma lista é mais rápida e mais eficiente em termos de memória. O motivo é que a junção precisa fazer duas passagens pelos dados, portanto, na verdade, precisa de uma lista real. Se você der um, ele pode começar seu trabalho imediatamente. Se, em vez disso, você fornecer uma genexp, ela não poderá começar a trabalhar até que crie uma nova lista na memória executando a genexp até a exaustão:~ $ python -m timeit '"".join(str(n) for n in xrange(1000))' 1000 loops, best of 3: 335 usec per loop ~ $ python -m timeit '"".join([str(n) for n in xrange(1000)])' 1000 loops, best of 3: 288 usec per loop
O mesmo resultado é válido ao comparar itertools.imap versus map :
~ $ python -m timeit -s'from itertools import imap' '"".join(imap(str, xrange(1000)))' 1000 loops, best of 3: 220 usec per loop ~ $ python -m timeit '"".join(map(str, xrange(1000)))' 1000 loops, best of 3: 212 usec per loop
fonte
''.join()
precisa de 2 passagens no iterador para construir uma string?Seu segundo exemplo usa uma expressão geradora em vez de uma compreensão de lista. A diferença é que com a compreensão da lista, uma lista é totalmente construída e passada para
.join()
. Com a expressão geradora, os itens são gerados um a um e consumidos por.join()
. Este último usa menos memória e geralmente é mais rápido.Por acaso, o construtor de lista consumirá qualquer iterável, incluindo uma expressão geradora. Então:
[str(n) for n in xrange(10)]
é apenas "açúcar sintático" para:
list(str(n) for n in xrange(10))
Em outras palavras, uma compreensão de lista é apenas uma expressão geradora que se transforma em uma lista.
fonte
[str(x) for x in xrange(1000)]
262 useclist(str(x) for x in xrange(1000))
,: 304 usec.seq = PySequence_Fast(orig, "");
e essa é a única razão pela qual os iteradores são executados mais lentamente do que listas ou tuplas ao chamar str.join (). Você é bem-vindo para iniciar um bate-papo se quiser discuti-lo mais (sou o autor do PEP 289, o criador do opcode LIST_APPEND e aquele que otimizou o construtor list (), então eu tenho alguns familiaridade com o assunto).Como mencionado, é uma expressão geradora .
Da documentação:
fonte
Se estiver entre parênteses, mas não entre colchetes, é tecnicamente uma expressão geradora. Expressões geradoras foram introduzidas pela primeira vez no Python 2.4.
http://wiki.python.org/moin/Generators
A parte após a junção
( str(_) for _ in xrange(10) )
é, por si só, uma expressão geradora. Você poderia fazer algo como:mylist = (str(_) for _ in xrange(10)) ''.join(mylist)
e significa exatamente a mesma coisa que você escreveu no segundo caso acima.
Os geradores têm algumas propriedades muito interessantes, e a menos importante delas é que eles não acabam alocando uma lista inteira quando você não precisa de uma. Em vez disso, uma função como join "bombeia" os itens para fora da expressão do gerador, um de cada vez, fazendo seu trabalho nas pequenas partes intermediárias.
Em seus exemplos particulares, lista e gerador provavelmente não funcionam de maneira terrivelmente diferente, mas em geral, eu prefiro usar expressões de gerador (e até funções de gerador) sempre que posso, principalmente porque é extremamente raro um gerador ser mais lento do que uma lista completa materialização.
fonte
Isso é um gerador, em vez de uma compreensão de lista. Os geradores também são iteráveis, mas em vez de criar a lista inteira primeiro e depois passá-la para a junção, ele passa cada valor no xrange um por um, o que pode ser muito mais eficiente.
fonte
O argumento para sua segunda
join
chamada é uma expressão geradora. Ele produz um iterável.fonte