(Don’t worry, this isn’t another question about unpacking tuples.)
In python, a statement like
foo = bar = baz = 5 assigns the variables foo, bar, and baz to 5. It assigns these variables from left to right, as can be proved by nastier examples like
0] = foo =  Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'foo' is not defined foo = foo =  foo [[...]] foo [[...]] foo is foo Truefoo[
But the python language reference states that assignment statements have the form
(target_list "=")+ (expression_list | yield_expression)
and on assignment the
expression_list is evaluated first and then the assigning happens.
So how can the line
foo = bar = 5 be valid, given that
bar = 5 isn’t an
expression_list? How are these multiple assignments on one line getting parsed and evaluated? Am I reading the language reference wrong?
All credit goes to @MarkDickinson, who answered this in a comment:
(target_list "=")+, which means one or more copies. In
foo = bar = 5, there are two
(target_list "=")productions, and the
expression_listpart is just
target_list productions (i.e. things that look like
foo =) in an assignment statement get assigned, from left to right, to the
expression_list on the right end of the statement, after the
expression_list gets evaluated.
And of course the usual ‘tuple-unpacking’ assignment syntax works within this syntax, letting you do things like
0], moo, foo = moo, foo, boo = , ,  foo [[[[...]]]] foo is boo True foo is moo True foo is foo Truefoo, boo, moo = boo[
Mark Dickinson explained the syntax of what is happening, but the weird examples involving
foo show that the semantics can be counter-intuitive.
= is a right-associative operator which returns as a value the RHS of the assignment so when you write
x = y = 5,
y=5 is first evaluated (assigning 5 to
y in the process) and this value (5) is then assigned to
Before I read this question, I naively assumed that roughly the same thing happens in Python. But, in Python
= isn’t an expression (for example,
2 + (x = 5) is a syntax error). So Python must achieve multiple assignments in another way.
We can disassemble rather than guess:
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
See this for a description of the byte code instructions.
The first instruction pushes 5 onto the stack.
The second instruction duplicates it — so now the top of the stack has two 5s
STORE_NAME(name) “Implements name = TOS” according to the byte code documentation
x = 5 (the 5 on top of the stack), popping that 5 off the stack as it goes, after which
y = 5 with the other 5 on the stack.
The rest of the bytecode isn’t directly relevant here.
In the case of
foo = foo =  the byte-code is more complicated because of the lists but has a fundamentally similar structure. The key observation is that once the list
 is created and placed on the stack then the instruction
DUP_TOP doesn’t place another copy of
 on the stack, instead it places another reference to the list. In other words, at that stage the top two elements of the stack are aliases for the same list. This can be seen most clearly in the somewhat simpler case:
0] x = 5 y 5x = y = [
foo = foo =  is executed, the list
 is first assigned to
foo and then an alias of the same list is assigned to
foo. This is why it results in
foo being a circular reference.
An assignment statement evaluates the expression list (remember that this can be a single expression or a comma-separated list, the latter yielding a tuple) and assigns the single resulting object to each of the target lists, from left to right.
bar = 5 is not an expression. The multiple assignment is a separate statement from the assignment statement; the expression is everything to the right of the right-most
A good way to think about it is that the right-most
= is the major separator; everything to the right of it happens from left to right, and everything to the left of it happens from left to right as well.