Unirse a una lista:
>>> ''.join([ str(_) for _ in xrange(10) ])
'0123456789'
join
debe tomar un iterable.
Aparentemente, join
el argumento es [ str(_) for _ in xrange(10) ]
, y es una lista de comprensión .
Mira esto:
>>>''.join( str(_) for _ in xrange(10) )
'0123456789'
Ahora, join
el argumento es simplemente str(_) for _ in xrange(10)
no []
, pero el resultado es el mismo.
¿Por qué? ¿ str(_) for _ in xrange(10)
También produce una lista o un iterable?
python
list-comprehension
Alcott
fuente
fuente
join
probablemente esté escrito en C y, por lo tanto, se ejecute mucho más rápido que la comprensión de una lista ... ¡Tiempo de prueba!_
no tiene un significado especial, es un nombre de variable regular. A menudo se usa como un nombre desechable, pero este no es el caso (está usando la variable). Evitaría usarlo en un código (al menos de esta manera).Respuestas:
>>>''.join( str(_) for _ in xrange(10) )
Esto se denomina expresión generadora y se explica en PEP 289 .
La principal diferencia entre las expresiones generadoras y las listas por comprensión es que las primeras no crean la lista en la memoria.
Tenga en cuenta que hay una tercera forma de escribir la expresión:
''.join(map(str, xrange(10)))
fuente
( str(_) for _ in xrange(10) )
. Pero estaba confundido, ¿por qué()
se puede omitirjoin
, lo que significa que el código debería ser como `` '' .join ((str (_) para _ en xrange (10))), verdad?tup = 1, 2, 3; print(tup)
. Con eso en mente, el usofor
como parte de una expresión crea el generador y los paréntesis están ahí para distinguirlo de un bucle escrito incorrectamente.Los otros encuestados tenían razón al responder que había descubierto una expresión generadora (que tiene una notación similar a las listas por comprensión, pero sin los corchetes que la rodean).
En general, los genexps (como se les conoce cariñosamente) son más eficientes en memoria y más rápidos que las listas por comprensión.
SIN EMBARGO, en el caso de
''.join()
, la comprensión de una lista es más rápida y más eficiente en la memoria. La razón es que join necesita realizar dos pasadas sobre los datos, por lo que en realidad necesita una lista real. Si le da uno, puede comenzar a trabajar de inmediato. Si le da un genexp en su lugar, no puede comenzar a funcionar hasta que construya una nueva lista en la memoria ejecutando genexp hasta el agotamiento:~ $ 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
El mismo resultado es válido al 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
fuente
''.join()
necesita 2 pases sobre el iterador para construir una cadena?Su segundo ejemplo usa una expresión generadora en lugar de una lista de comprensión. La diferencia es que con la comprensión de la lista, una lista se construye y se pasa por completo
.join()
. Con la expresión del generador, los elementos se generan uno por uno y son consumidos por.join()
. Este último usa menos memoria y generalmente es más rápido.Da la casualidad de que el constructor de listas consumirá felizmente cualquier iterable, incluida una expresión generadora. Entonces:
[str(n) for n in xrange(10)]
es simplemente "azúcar sintáctico" para:
list(str(n) for n in xrange(10))
En otras palabras, una lista de comprensión es solo una expresión generadora que se convierte en una lista.
fuente
[str(x) for x in xrange(1000)]
262 usec ,:list(str(x) for x in xrange(1000))
304 usec.seq = PySequence_Fast(orig, "");
y esa es la única razón por la que los iteradores se ejecutan más lentamente que las listas o tuplas al llamar a str.join (). Puede iniciar un chat si desea discutirlo más a fondo (soy el autor de PEP 289, el creador del código de operación LIST_APPEND y el que optimizó el constructor list (), así que tengo algunos familiaridad con el tema).Como se mencionó, es una expresión generadora .
De la documentación:
fuente
Si está entre paréntesis, pero no entre paréntesis, técnicamente es una expresión generadora. Las expresiones generadoras se introdujeron por primera vez en Python 2.4.
http://wiki.python.org/moin/Generators
La parte que
( str(_) for _ in xrange(10) )
sigue a la combinación es, por sí misma, una expresión generadora. Podrías hacer algo como:mylist = (str(_) for _ in xrange(10)) ''.join(mylist)
y significa exactamente lo mismo que escribió en el segundo caso anterior.
Los generadores tienen algunas propiedades muy interesantes, una de las cuales es que no terminan asignando una lista completa cuando no la necesitas. En cambio, una función como join "bombea" los elementos de la expresión del generador de uno en uno, haciendo su trabajo en las pequeñas partes intermedias.
En sus ejemplos particulares, la lista y el generador probablemente no funcionen de manera muy diferente, pero en general, prefiero usar expresiones de generador (e incluso funciones de generador) siempre que pueda, principalmente porque es extremadamente raro que un generador sea más lento que una lista completa. materialización.
fuente
Eso es un generador, en lugar de una lista de comprensión. Los generadores también son iterables, pero en lugar de crear primero la lista completa y luego pasarla para unirse, pasa cada valor en el rango x uno por uno, lo que puede ser mucho más eficiente.
fuente
El argumento de su segunda
join
llamada es una expresión generadora. Produce un iterable.fuente