Desembalaje generalizaciones

>>> LOL = [[1, 2], ['three']] >>> [*LOL[0], *LOL[1]] [1, 2, 'three'] 

¡Bien! Adiós itertools.chain . Nunca me gustaste mucho de todos modos.

 >>> [*L for L in LOL] File "", line 1 [*L for L in LOL] ^ SyntaxError: iterable unpacking cannot be used in comprehension 

Oh ¿Por qué no podemos tener cosas buenas?

Desembalar en una comprensión parece ser obvio / pythonic, pero dado que se molestaron en agregar ese mensaje de error especial, había una razón para deshabilitarlo. Entonces, ¿cuál es el problema con esa syntax?

Tomando una cita del hilo de la lista de correo Py-Dev en el que se aceptó esta función :

Así que eso deja comprensiones. IIRC, durante el desarrollo del parche, nos dimos cuenta de que f(*x for x in xs) es lo suficientemente ambiguo que decidimos rechazarlo. Tenga en cuenta que f(x for x in xs) ya es un caso especial porque un argumento solo puede ser una expresión generadora “simple” si es el único argumento. El mismo razonamiento no se aplica (en esa forma) a la lista, el conjunto y dictados de comprensión, mientras que f(x for x in xs) es idéntico en significado a f((x for x in xs)) , [x for x in xs] NO es lo mismo que [(x for x in xs)] (es una lista de un elemento, y el elemento es una expresión generadora)

(Énfasis mío)

También eché un vistazo al rastreador de problemas de Python para esta característica. Encontré un problema en el cual la discusión tuvo lugar mientras se implementaba. La secuencia de mensajes que les ayudó a llegar a esta realización comienza aquí con una descripción general de la ambigüedad introducida presentada en msg234766 por GvR.

Por miedo a link-rot, adjunto el mensaje (formateado) aquí:

Así que creo que la función de prueba aquí debería ser:

 def f(*a, **k): print(list(a), list(k)) 

Entonces podemos probar cosas como:

 f(x for x in ['ab', 'cd']) 

que imprime un objeto generador, porque esto se interpreta como un argumento que es una expresión generadora.

Pero ahora consideremos:

 f(*x for x in ['ab', 'cd']) 

Personalmente esperaba que esto fuera equivalente a:

 f(*'ab', *'cd') 

IOW:

  f('a', 'b', 'c', 'd') 

El PEP no da claridad sobre qué hacer aquí. La pregunta ahora es, ¿deberíamos interpretar cosas como *x for x in ... como una forma extendida de expresión generadora, o como una forma extendida de *arg ? De alguna manera creo que lo último es más útil y también la extensión más lógica.

Mi razonamiento es que el PEP admite cosas como f(*a, *b) y sería bastante lógico interpretar que f(*x for x in xs) hace la cosa *x para cada x en la lista xs .

Finalmente, como se señaló en la sección Resumen del PEP correspondiente , esta función no se descarta por completo:

Este PEP no incluye el desempaquetado de operadores dentro de la lista, el conjunto y las comprensiones del diccionario, aunque esto no se ha descartado para futuras propuestas .

Por lo tanto, podríamos verlo pronto (definitivamente no 3.6, sin embargo 🙂 y espero que lo hagamos, se ven bien.

Esto se explica brevemente en el PEP 448 que introduce generalizaciones de desempaquetado:

Las iteraciones anteriores de este PEP permitieron desempaquetar a los operadores dentro de la lista, conjunto y comprensión del diccionario como operador de aplanamiento sobre iterables de contenedores:

 >>> ranges = [range(i) for i in range(5)] >>> [*item for item in ranges] [0, 0, 1, 0, 1, 2, 0, 1, 2, 3] >>> {*item for item in ranges} {0, 1, 2, 3} 

Esto se encontró con una mezcla de fuertes preocupaciones sobre la legibilidad y el apoyo leve. Para no poner en desventaja los aspectos menos controvertidos de la PEP, esto no fue aceptado con el rest de la propuesta.

Sin embargo, esto puede cambiar en el futuro:

Este PEP no incluye el desempaquetado de operadores dentro de la lista, el conjunto y las comprensiones del diccionario, aunque esto no se ha descartado para futuras propuestas.