What is this kind of assignment in Python called? a = b = True

Question:

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 me up a bit especially when the RHS is mutable, but I’m having real trouble finding the right keywords to search for in the docs.

Asked By: pfctdayelise

||

Answers:

It’s a chain of assignments and the term used to describe it is…

– Could I get a drumroll please?

Chained Assignment.


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:

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 expression f(), 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 of i.

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.

Answered By: pfctdayelise

@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.

Answered By: PaulMcG

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++.

Answered By: qeatzy
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.