python - Forma pitónica de descomprimir un iterador dentro de una lista
python-3.x unpack (5)
Estoy tratando de descubrir cuál es la forma pythonic de desempaquetar un iterador dentro de una lista.
Por ejemplo:
my_iterator = zip([1, 2, 3, 4], [1, 2, 3, 4])
He venido con las siguientes formas de descomprimir mi iterador dentro de una lista:
1)
my_list = [*my_iterator]
2)
my_list = [e for e in my_iterator]
3)
my_list = list(my_iterator)
No 1) es mi forma favorita de hacerlo, ya que es menos código, pero me pregunto si esta también es la forma pitónica. ¿O tal vez hay otra forma de lograr esto además de las 3, que es la forma pitónica?
Después de explorar más el tema, he llegado a algunas conclusiones.
Debe haber una, y preferiblemente solo una, forma obvia de hacerlo
( pythonic )
Decidir qué opción es la "pitónica" debería tener en cuenta algunos criterios:
- cuan explícito
- sencillo,
- y legible es.
Y la opción obvia "pitónica" que gana en todos los criterios es la opción número 3):
lista = lista (mi_iterador)
Aquí es por qué es "obvio" que no 3) es el pitónico:
- La opción 3) está cerca del lenguaje natural, lo que te hace pensar "instantáneamente" cuál es el resultado.
- Opción 2) (usando la comprensión de la lista) si ve por primera vez esa línea de código que lo llevará a leer un poco más y a prestar un poco más de atención. Por ejemplo, uso la comprensión de la lista cuando quiero agregar algunos pasos adicionales (llamar a una función con los elementos iterados o hacer algunas comprobaciones usando la instrucción if), así que cuando veo una comprensión de la lista, verifico si hay alguna llamada a la función dentro o para cualquier si declaración
-
opción 1) (desempacar usando *) el operador de asterisco puede ser un poco confuso si no lo usa regularmente, hay 4 casos para usar el asterisco en Python :
- Para operaciones de multiplicación y potencia.
- Para extender repetidamente los contenedores de tipo lista.
- Para usar los argumentos variadic. (llamado "embalaje")
- Para desempacar los contenedores.
Otro buen argumento son los
documentos de Python
, he hecho algunas estadísticas para verificar qué opciones son elegidas por los documentos, para esto he elegido 4 iteradores
itertools
y todo, desde las
itertools
módulo (que se usan como:
itertools.
)
itertools.
vea cómo se desempaquetan en una lista:
- mapa
- distancia
- filtrar
- enumerar
- itertools.
Después de explorar los documentos que encontré: 0 iteradores desempaquetados en una lista usando la opción 1) y 2) y 35 usando la opción 3).
Conclusión
La forma pitónica de desempaquetar un iterador dentro de una lista es:
my_list = list(my_iterator)
Esto podría ser una repetición de la
forma más rápida de convertir un iterador en una lista
, pero su pregunta es un poco diferente ya que pregunta cuál es la más pitónica.
La respuesta aceptada es
list(my_iterator)
sobre
[e for e in my_iterator]
porque el anterior se ejecuta en C debajo del capó.
Un comentarista sugiere que
[*my_iterator]
es más rápido que
list(my_iterator)
, por lo que es posible que desee probar eso.
Mi voto general es que todos son igualmente pitónicos, así que iría con el más rápido de los dos para su caso de uso.
También es posible que la respuesta anterior esté desactualizada.
Si bien el operador de desempaquetado
*
no se usa a menudo para desempaquetar un solo iterable en una lista (por lo tanto,
[*it]
es un poco menos legible que
list(it)
), es útil y más Pythonic en varios otros casos:
1. Desempaquetar un iterable en una sola lista / tupla / conjunto, agregando otros valores:
mixed_list = [a, *it, b]
Esto es más conciso y eficiente que
mixed_list = [a]
mixed_list.extend(it)
mixed_list.append(b)
2. Desempaquetar múltiples iterables + valores en una lista / tupla / conjunto
mixed_list = [*it1, *it2, a, b, ... ]
Esto es similar al primer caso.
3. Desempaquetar un iterable en una lista, excluyendo elementos
first, *rest = it
Esto extrae el primer elemento y lo descomprime en una lista. Incluso se puede hacer
_, *mid, last = it
Esto convierte el primer elemento en una variable que no importa, guarda el último elemento en el
last
y descomprime el resto en una lista
mid
.
4. Desempaquetado anidado de múltiples niveles de un iterable en una declaración
it = (0, range(5), 3)
a1, (*a2,), a3 = it # Unpack the second element of it into a list a2
e1, (first, *rest), e3 = it # Separate the first element from the rest while unpacking it[1]
Esto también se puede usar
for
declaraciones:
from itertools import groupby
s = "Axyz123Bcba345D"
for k, (first, *rest) in groupby(s, key=str.isalpha):
...
Si está interesado en la menor cantidad de tipeo posible, en realidad puede hacer un carácter mejor que
my_list = [*my_iterator]
con un desempaquetado iterable:
*my_list, = my_iterator
o (aunque esto solo es igual a
my_list = [*my_iterator]
en el número de caracteres):
[*my_list] = my_iterator
(Es curioso cómo tiene el mismo efecto que
my_list = [*my_iterator]
).
Sin embargo, para la solución más Pythonic,
my_list = list(my_iterator)
es claramente la más clara y legible de todas, y por lo tanto debería considerarse la más Pythonic.
Tiendo a usar zip si necesito convertir una lista a un diccionario o usarlo como un par clave-valor en un bucle o comprensión de lista.
Sin embargo, si esto es solo una ilustración para crear un iterador. Definitivamente votaré por el # 3 por claridad.