¿Python3.5 tuple comprensión realmente esta limitada?

Me han encantado las integraciones de tuplas añadidas a Python3.5:

In [128]: *(x for x in range(5)), Out[128]: (0, 1, 2, 3, 4) 

Sin embargo, cuando bash return una comprensión de tupla directamente, aparece un error:

 In [133]: def testFunc(): ...: return *(x for x in range(5)), ...: File "", line 2 return *(x for x in range(5)), ^ SyntaxError: invalid syntax 

Esto es solo un pequeño inconveniente ya que simplemente puedo asignar la comprensión de la tupla a una variable y devolver la variable. Sin embargo, si trato de poner una comprensión de tupla dentro de una comprensión de diccionario, obtengo el mismo error:

 In [130]: {idx: *(x for x in range(5)), for idx in range(5)} File "", line 1 {idx: *(x for x in range(5)), for idx in range(5)} ^ SyntaxError: invalid syntax 

Siento que esto es un poco más de un problema ya que la comprensión puede ser importante para el desempeño en algunas situaciones.

No tengo ningún problema con el uso del diccionario y la lista de comprensión en estas situaciones. ¿Cuántas otras situaciones no funcionará la comprensión de la tupla cuando lo hacen otras personas? O tal vez lo estoy usando mal?

Me pregunto de qué se trata si su uso es tan limitado o quizás esté haciendo algo mal. Si no estoy haciendo algo mal, ¿cuál es la forma más rápida / más python de crear una tupla que sea lo suficientemente versátil como para usarla de la misma manera que las comprensiones de listas y diccionarios?

TLDR: Si desea una tupla, pase una expresión generadora a tuple :

 {idx: tuple(x for x in range(5)) for idx in range(5)} 

No hay “comprensión de la tupla” en Python. Esta:

 x for x in range(5) 

Es una expresión generadora. Agregar paréntesis a su alrededor simplemente se usa para separarlo de otros elementos. Esto es lo mismo que en (a + b) * c , que tampoco implica una tupla.

El símbolo * es para el empaquetado / desempaquetado del iterador . Resulta que una expresión generadora es iterable, por lo que se puede desempaquetar. Sin embargo, debe haber algo para descomprimir el iterable en . Por ejemplo, también se puede descomprimir una lista en los elementos de una asignación:

 *[1, 2] # illegal - nothing to unpack into a, b, c, d = *[1, 2], 3, 4 # legal - unpack into assignment tuple 

Ahora, haciendo *, combina * desempaquetar con un , tuple literal. Sin embargo, esto no es utilizable en todas las situaciones: los elementos de separación pueden tener prioridad sobre la creación de una tupla. Por ejemplo, el último , en [*(1, 2), 3] separa, mientras que en [(*(1, 2), 3)] crea una tupla.

En un diccionario el , es ambiguo ya que se utiliza para separar elementos. Compare {1: 1, 2: 2} y tenga en cuenta que {1: 2,3} es ilegal. Para una statement de return , podría ser posible en el futuro .

Si quieres una tupla, debes usar () siempre que haya ambigüedad, incluso si Python puede manejarlo, es difícil analizar para los humanos de lo contrario.

Cuando su fuente es una statement grande, como una expresión generadora, sugiero convertir explícitamente a una tupla. Compare las siguientes dos versiones válidas de su código para facilitar la lectura:

 {idx: tuple(x for x in range(5)) for idx in range(5)} {idx: (*(x for x in range(5)),) for idx in range(5)} 

Tenga en cuenta que las comprensiones de list y dict también funcionan de manera similar: son prácticamente como pasar una expresión de generador para list , set o dict . En su mayoría sirven para evitar buscar list , set o dict en el espacio de nombres global.


Siento que esto es un poco más de un problema ya que la comprensión puede ser importante para el desempeño en algunas situaciones.

Debajo de las cubiertas, tanto las expresiones del generador como las comprensiones de lista / dictado / conjunto crean una función de corta duración. No debe confiar en las comprensiones para la optimización del rendimiento a menos que las haya perfilado y probado. Por defecto, use lo que sea más legible para su caso de uso.

 dis.dis("""[a for a in (1, 2, 3)]""") 1 0 LOAD_CONST 0 ( at 0x10f730ed0, file "", line 1>) 2 LOAD_CONST 1 ('') 4 MAKE_FUNCTION 0 6 LOAD_CONST 5 ((1, 2, 3)) 8 GET_ITER 10 CALL_FUNCTION 1 12 RETURN_VALUE 

Pase una expresión del generador al constructor tuple() , ya que no hay comprensión de tuplas :

 {idx: tuple(x for x in range(5)) for idx in range(5)} 

Las comprensiones de tuplas no existen, pero aunque las comprensiones de listas sí ( [... for ... in ...] ) son similares a *: list(... for ... in ...) .


* las comprensiones de listas son en realidad más rápidas que una expresión generadora que se pasa a una función constructora, ya que la ejecución de funciones es costosa en Python