I know about tuple unpacking but what is this assignment called where you have multiple equals signs on a single line? a la a = b = True
It always trips
OK, "chained assignment" was the search term I was after, but after a bit more digging I think it's not strictly correct. but it is easier to search for than "a special case of the assignment statement".
The Wikipedia article senderle linked to says:
In Python, assignment statements are not expressions and thus do not return a value. Instead, chained assignments are a series of statements with multiple targets for a single expression. The assignments are executed left-to-right so that
i = arr[i] = f()
evaluates the expressionf()
, then assigns the result to the leftmost target,i
, and then assigns the same result to the next target,arr[i]
, using the new value ofi
.
Another blog post says:
In Python, assignment statements do not return a value. Chained assignment (or more precisely, code that looks like chained assignment statements) is recognized and supported as a special case of the assignment statement.
This seems the most correct to me, on a closer reading of the docs - in particular (target_list "=")+
- which also say
An assignment statement evaluates the expression list ... and assigns the single resulting object to each of the target lists, from left to right.
So it's not really "evaluated from right-most to left" - the RHS is evaluated and then assigned from left-most target to right - not that I can think of any real-world (or even contrived) examples where it would make a difference.
It's a chain of assignments and the term used to describe it is...
- Could I get a drumroll please?
I just gave it a quite google run and found that there isn't that much to read on the topic, probably since most people find it very straight-forward to use (and only the true geeks would like to know more about the topic).
In the previous expression the order of evaluation can be viewed as starting at the right-most =
and then working towards the left, which would be equivalent of writing:
b = True
a = b
The above order is what most language describe an assignment-chain, but python does it differently. In python the expression is evaluated as this below equivalent, though it won't result in any other result than what is previously described.
temporary_expr_result = True
a = temporary_expr_result
b = temporary_expr_result
Further reading available here on stackoverflow:
got bitten by python's Chained Assignment today, due to my ignorance. in code
if l1.val <= l2.val:
tail = tail.next = l1 # this line
l1 = l1.next
what I expected was
tail.next = l1
tail = tail.next
# or equivalently
# tail = l1
whereas I got below, which produce a self loop in the list, leave me in a endless loop, whoops...
tail = l1
tail.next = l1 # now l1.next is changed to l1 itself
since for a = b = c, one way (python, for example) equivalent to
tmp = evaluate(c)
evaluate(a) = tmp
evaluate(b) = tmp
and have equal right operand for two assignment.
the other (C++, for example) equivalent to
evaluate(b) = evaluate(c)
evaluate(a) = evaluate(b)
since in this case a = b = c
is basically
b = c
a = b
and two right hand operand could be different.
That why similar code works well in C++.
@refp's answer is further supported with this output using the dis
(disassembly) module:
>>> def a(x):
... g = h = x
...
>>> import dis
>>> dis.dis(a)
2 0 LOAD_FAST 0 (x)
3 DUP_TOP
4 STORE_FAST 1 (g)
7 STORE_FAST 2 (h)
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
The RHS is retrieved and duplicated, then stored into the destination variables left-to-right (try this yourself with e = f = g = h = x
).
Some other posters have been confused if the RHS is a function call, like a = b = fn()
- the RHS is only evaluated once, and then the result assigned to each successive variable. This may cause unwanted sharing if the returned value is a mutable, like a list or dict.
For those using threading
, it is useful to note that there is no "atomicity" implied by the chained assignment form over multiple explicit assignment statements - a thread switch could occur between the assignments to g and h, and another thread looking at the two of them could see different values in the two variables.
From the documentation, 7.2. Assignment statements, g
and h
being two target lists, x
being the expression list:
assignment_stmt ::= (target_list "=")+ (expression_list | yield_expression)
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.