¿Por qué no hay comprensión de la tupla en Python?

Como todos sabemos, hay una lista de comprensión, como

[i for i in [1, 2, 3, 4]] 

y hay diccionario de comprensión, como

 {i:j for i, j in {1: 'a', 2: 'b'}.items()} 

pero

 (i for i in (1, 2, 3)) 

Terminará en un generador, no en una comprensión de la tuple . ¿Porqué es eso?

Mi conjetura es que una tuple es inmutable, pero esta no parece ser la respuesta.

Puedes usar una expresión generadora:

 tuple(i for i in (1, 2, 3)) 

pero ya se tomaron paréntesis para … expresiones del generador.

Raymond Hettinger (uno de los desarrolladores principales de Python) dijo esto acerca de las tuplas en un tweet reciente :

#python tip: Generalmente, las listas son para bucles; Tuplas para estructuras. Las listas son homogéneas; Tuplas heterogéneas.Listas de longitud variable.

Esto (para mí) apoya la idea de que si los elementos en una secuencia están lo suficientemente relacionados como para ser generados por un generador, bueno, entonces debería ser una lista. Aunque una tupla es iterable y parece simplemente una lista inmutable, en realidad es el equivalente de Python de una estructura C:

 struct { int a; char b; float c; } foo; struct foo x = { 3, 'g', 5.9 }; 

se convierte en Python

 x = (3, 'g', 5.9) 

Desde Python 3.5 , también puede usar la syntax de desempaquetado de splat * para desempaquetar una expresión del generador:

 *(x for x in range(10)), 

La comprensión funciona en bucle o iteración sobre elementos y asignándolos a un contenedor, una tupla no puede recibir asignaciones.

Una vez que se crea un Tuple, no se puede agregar, ampliar o asignar. La única forma de modificar una tupla es si uno de sus objetos puede asignarse (no es un contenedor de tuplas). Porque el Tuple solo tiene una referencia a ese tipo de objeto.

Además, una tupla tiene su propia tuple() constructor tuple() que puede proporcionar a cualquier iterador. Lo que significa que para crear una tupla, podrías hacer:

 tuple(i for i in (1,2,3)) 

Como mencionó otro póster de macm , la forma más rápida de crear una tupla a partir de un generador es la tuple([generator]) .


Comparación de rendimiento

  • Lista de comprensión:

     $ python3 -m timeit "a = [i for i in range(1000)]" 10000 loops, best of 3: 27.4 usec per loop 
  • Tuple de la lista de comprensión:

     $ python3 -m timeit "a = tuple([i for i in range(1000)])" 10000 loops, best of 3: 30.2 usec per loop 
  • Tuple del generador:

     $ python3 -m timeit "a = tuple(i for i in range(1000))" 10000 loops, best of 3: 50.4 usec per loop 
  • Tuple de desembalaje:

     $ python3 -m timeit "a = *(i for i in range(1000))," 10000 loops, best of 3: 52.7 usec per loop 

Mi versión de python :

 $ python3 --version Python 3.6.3 

Por lo tanto, siempre debe crear una tupla a partir de una lista de comprensión, a menos que el rendimiento no sea un problema.

Mi mejor conjetura es que se quedaron sin corchetes y no pensaron que sería lo suficientemente útil como para justificar la adición de una syntax “fea” …

Las tuplas no pueden ser anexadas eficientemente como una lista.

Por lo tanto, una comprensión de la tupla tendría que usar una lista internamente y luego convertirla en una tupla.

Eso sería lo que haces ahora: tuple ([comprensión])

Los paréntesis no crean una tupla. aka uno = (dos) no es una tupla. La única forma de evitarlo es uno = (dos) o uno = tupla (dos). Así que una solución es:

 tuple(i for i in myothertupleorlistordict) 

Creo que es simplemente por claridad, no queremos saturar el lenguaje con demasiados símbolos diferentes. Además, una comprensión de la tuple nunca es necesaria , una lista solo se puede utilizar en su lugar con diferencias de velocidad insignificantes, a diferencia de una comprensión de dictado en lugar de una comprensión de lista.

Podemos generar tuplas a partir de una lista de comprensión. El siguiente agrega dos números secuencialmente en una tupla y da una lista de los números 0-9.

 >>> print k [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99] >>> r= [tuple(k[i:i+2]) for i in xrange(10) if not i%2] >>> print r [(0, 1), (2, 3), (4, 5), (6, 7), (8, 9)]