¿Por qué no es un error asignar a una lista vacía (por ejemplo, [] = “”)?

110

En Python 3.4, estoy escribiendo

[] = "" 

y funciona bien, no se genera ninguna excepción. Aunque, por supuesto, []no es igual a ""después.

[] = ()

también funciona bien.

"" = []

sin embargo, genera una excepción como se esperaba,

() = ""

Sin embargo, genera una excepción como se esperaba. Entonces, ¿qué está pasando?

Vardd
fuente

Respuestas:

132

No estás comparando por igualdad. Estás asignando .

Python le permite asignar a múltiples objetivos:

foo, bar = 1, 2

asigna los dos valores a fooy bar, respectivamente. Todo lo que necesita es una secuencia o iterable en el lado derecho y una lista o tupla de nombres a la izquierda.

Cuando tu lo hagas:

[] = ""

asignó una secuencia vacía (las cadenas vacías siguen siendo secuencias) a una lista vacía de nombres.

Es esencialmente lo mismo que hacer:

[foo, bar, baz] = "abc"

donde terminas con foo = "a", bar = "b"y baz = "c", pero con menos caracteres.

Sin embargo, no puede asignar a una cadena, por lo que ""en el lado izquierdo de una asignación nunca funciona y siempre hay un error de sintaxis.

Ver el documentación de las declaraciones de asignación :

Una declaración de asignación evalúa la lista de expresiones (recuerde que puede ser una expresión única o una lista separada por comas, la última produce una tupla) y asigna el objeto resultante a cada una de las listas de destino, de izquierda a derecha.

y

Asignación de un objeto a una lista de objetivos, opcionalmente entre paréntesis o corchetes , se define recursivamente como sigue.

Énfasis mío .

¡Que Python no arroje un error de sintaxis para la lista vacía es en realidad un error! La gramática oficialmente documentada no permite una lista de objetivos vacía, y para la vacía ()se obtiene un error. Ver error 23275 ; se considera un error inofensivo:

El punto de partida es reconocer que esto ha existido durante mucho tiempo y es inofensivo.

Consulte también ¿Por qué es válido asignar a una lista vacía pero no a una tupla vacía?

Martijn Pieters
fuente
36

Sigue las reglas de la sección Declaraciones de asignación de la documentación,

assignment_stmt ::=  (target_list "=")+ (expression_list | yield_expression)

Si target listes una lista de destinos separados por comas: el objeto debe ser iterable con el mismo número de elementos que hay en la lista de destinos y los elementos se asignan, de izquierda a derecha, a los destinos correspondientes.

El objeto debe ser una secuencia con el mismo número de elementos que los objetivos que hay en la lista de objetivos, y los elementos se asignan, de izquierda a derecha, a los objetivos correspondientes.

Entonces, cuando dices

[] = ""

"" es iterable (cualquier cadena de Python válida es iterable) y se está desempaquetando sobre los elementos de la lista.

Por ejemplo,

>>> [a, b, c] = "123"
>>> a, b, c
('1', '2', '3')

Dado que tiene una cadena vacía y una lista vacía, no hay nada que descomprimir. Entonces, no hay error.

Pero prueba esto

>>> [] = "1"
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: too many values to unpack (expected 0)
>>> [a] = ""
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: need more than 0 values to unpack

En el [] = "1"caso, está intentando descomprimir la cadena "1"sobre una lista vacía de variables. Entonces se queja con "demasiados valores para descomprimir (esperado 0)".

De la misma manera, en [a] = "" caso de que tenga una cadena vacía, no hay nada que descomprimir realmente, pero lo está descomprimiendo en una variable, lo cual, nuevamente, no es posible. Es por eso que se queja de que "se necesitan más de 0 valores para descomprimir".

Aparte de eso, como habrás notado,

>>> [] = ()

tampoco arroja ningún error, porque ()es una tupla vacía.

>>> ()
()
>>> type(())
<class 'tuple'>

y cuando se desempaqueta sobre una lista vacía, no hay nada que descomprimir. Así que no hay error.


Pero, cuando lo haces

>>> "" = []
  File "<input>", line 1
SyntaxError: can't assign to literal
>>> "" = ()
  File "<input>", line 1
SyntaxError: can't assign to literal

como dice el mensaje de error, está intentando asignar a una cadena literal. Lo cual no es posible. Es por eso que está recibiendo los errores. Es como decir

>>> 1 = "one"
  File "<input>", line 1
SyntaxError: can't assign to literal

Internos

Internamente, esta operación de asignación se traducirá al UNPACK_SEQUENCEcódigo de operación ,

>>> dis(compile('[] = ""', "string", "exec"))
  1           0 LOAD_CONST               0 ('')
              3 UNPACK_SEQUENCE          0
              6 LOAD_CONST               1 (None)

Aquí, dado que la cadena está vacía, UNPACK_SEQUENCEdescomprime los 0tiempos. Pero cuando tienes algo como esto

>>> dis(compile('[a, b, c] = "123"', "string", "exec"))
  1           0 LOAD_CONST               0 ('123')
              3 UNPACK_SEQUENCE          3
              6 STORE_NAME               0 (a)
              9 STORE_NAME               1 (b)
             12 STORE_NAME               2 (c)
             15 LOAD_CONST               1 (None)
             18 RETURN_VALUE

la secuencia 123se desempaqueta en la pila, de derecha a izquierda. Entonces, la parte superior de la pila sería 1y la siguiente sería 2y la última sería 3. Luego asigna desde la parte superior de la pila a las variables de la expresión del lado izquierdo una por una.


Por cierto, en Python, así es como puede hacer múltiples asignaciones en la misma expresión. Por ejemplo,

a, b, c, d, e, f = u, v, w, x, y, z

esto funciona porque los valores de la derecha se utilizan para construir una tupla y luego se descomprime sobre los valores de la izquierda.

>>> dis(compile('a, b, c, d, e, f = u, v, w, x, y, z', "string", "exec"))
  1           0 LOAD_NAME                0 (u)
              3 LOAD_NAME                1 (v)
              6 LOAD_NAME                2 (w)
              9 LOAD_NAME                3 (x)
             12 LOAD_NAME                4 (y)
             15 LOAD_NAME                5 (z)
             18 BUILD_TUPLE              6
             21 UNPACK_SEQUENCE          6
             24 STORE_NAME               6 (a)
             27 STORE_NAME               7 (b)
             30 STORE_NAME               8 (c)
             33 STORE_NAME               9 (d)
             36 STORE_NAME              10 (e)
             39 STORE_NAME              11 (f)
             42 LOAD_CONST               0 (None)
             45 RETURN_VALUE

pero la técnica de intercambio clásica a, b = b, ausa la rotación de elementos en la parte superior de la pila. Si usted tiene sólo dos o tres elementos, entonces se trata con especial ROT_TWOe ROT_THREEinstrucciones en lugar de construir la tupla y desembalaje.

>>> dis(compile('a, b = b, a', "string", "exec"))
  1           0 LOAD_NAME                0 (b)
              3 LOAD_NAME                1 (a)
              6 ROT_TWO
              7 STORE_NAME               1 (a)
             10 STORE_NAME               0 (b)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE
los cuatro ojos
fuente
También puede usar dis('[] = ""')sin llamar compile().
Andrea Corbellini
¿Puede describir qué sucede si intercambia más de tres variables / elementos, utilizando el método en su último ejemplo?
nanofaradio
@hexafraction Construirá una nueva tupla con todos los elementos en el lado derecho y luego los descomprimirá sobre las variables en el lado izquierdo.
thefourtheye
@hexafraction: consulte ¿Cómo funciona
Martijn Pieters