python python-3.x list unpack

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 :

    1. Para operaciones de multiplicación y potencia.
    2. Para extender repetidamente los contenedores de tipo lista.
    3. Para usar los argumentos variadic. (llamado "embalaje")
    4. 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.