simbolos lista leer iteraciones funciones for ejemplos declarar contador como python variable-assignment

lista - Sentencias de asignación múltiple de Python en una línea



lista de funciones de python (4)

Mark Dickinson explicó la sintaxis de lo que está sucediendo, pero los ejemplos extraños que involucran a foo muestran que la semántica puede ser contraintuitiva.

En C, = es un operador asociativo a la derecha que devuelve como un valor la RHS de la asignación, de modo que cuando escribe x = y = 5 , y=5 se evalúa primero (asignando 5 a y en el proceso) y este valor (5 ) entonces se asigna a x .

Antes de leer esta pregunta, asumí ingenuamente que en Python ocurre lo mismo. Pero, en Python = no es una expresión (por ejemplo, 2 + (x = 5) es un error de sintaxis). Entonces Python debe lograr múltiples tareas de otra manera.

Podemos desmontar en lugar de adivinar:

>>> import dis >>> dis.dis(''x = y = 5'') 1 0 LOAD_CONST 0 (5) 3 DUP_TOP 4 STORE_NAME 0 (x) 7 STORE_NAME 1 (y) 10 LOAD_CONST 1 (None) 13 RETURN_VALUE

Vea this para una descripción de las instrucciones del código de bytes.

La primera instrucción empuja 5 en la pila.

La segunda instrucción lo duplica, así que ahora la parte superior de la pila tiene dos 5s

STORE_NAME(name) "Implements name = TOS" de acuerdo con la documentación del código de bytes

Así, STORE_Name(x) implementa x = 5 (el 5 en la parte superior de la pila), sacando ese 5 de la pila a medida que avanza, después de lo cual STORE_Name(y) implementa y = 5 con los otros 5 en la pila.

El resto del código de bytes no es directamente relevante aquí.

En el caso de foo = foo[0] = [0] el código de bytes es más complicado debido a las listas pero tiene una estructura fundamentalmente similar. La observación clave es que una vez que la lista [0] se crea y se coloca en la pila, la instrucción DUP_TOP no coloca otra copia de [0] en la pila, sino que coloca otra referencia a la lista. En otras palabras, en esa etapa, los dos elementos superiores de la pila son alias para la misma lista. Esto se puede ver más claramente en el caso algo más simple:

>>> x = y = [0] >>> x[0] = 5 >>> y[0] 5

Cuando se ejecuta foo = foo[0] = [0] , la lista [0] se asigna primero a foo y luego se asigna un alias de la misma lista a foo[0] . Por eso resulta que foo es una referencia circular.

(No se preocupe, esta no es otra pregunta sobre cómo desempacar las tuplas).

En python, una declaración como foo = bar = baz = 5 asigna las variables foo, bar y baz a 5. Asigna estas variables de izquierda a derecha, como lo demuestran los ejemplos más desagradables como

>>> foo[0] = foo = [0] Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name ''foo'' is not defined >>> foo = foo[0] = [0] >>> foo [[...]] >>> foo[0] [[...]] >>> foo is foo[0] True

Pero la referencia del lenguaje python establece que las declaraciones de asignación tienen la forma

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

y en la asignación, la expression_list se evalúa primero y luego se realiza la asignación.

Entonces, ¿cómo puede la línea foo = bar = 5 ser válida, dado que bar = 5 no es una expression_list ? ¿Cómo se analizan y evalúan estas múltiples tareas en una línea? ¿Estoy leyendo mal la referencia de lenguaje?


Todo el crédito es para @MarkDickinson, quien respondió esto en un comentario:

Observe el + en (target_list "=")+ , que significa una o más copias. En foo = bar = 5 , hay dos (target_list "=") , y la parte expression_list es solo 5

Todas target_list producciones de target_list (es decir, las cosas que parecen foo = ) en una declaración de asignación se asignan, de izquierda a derecha, a la expression_list en el extremo derecho de la declaración, después de expression_list se evalúe la lista_expresión.

Y, por supuesto, la sintaxis de asignación habitual de ''desempaquetar tuplas'' funciona dentro de esta sintaxis, permitiéndole hacer cosas como

>>> foo, boo, moo = boo[0], moo[0], foo[0] = moo[0], foo[0], boo[0] = [0], [0], [0] >>> foo [[[[...]]]] >>> foo[0] is boo True >>> foo[0][0] is moo True >>> foo[0][0][0] is foo True



bar = 5 no es una expresión. La asignación múltiple es una declaración separada de la declaración de asignación; la expresión es todo a la derecha de la extrema derecha = .

Una buena manera de pensarlo es que el más a la derecha = es el separador principal; Todo a la derecha sucede de izquierda a derecha, y todo a la izquierda sucede de izquierda a derecha también.