I have some code like this:
def foo():
bar = initial_bar = Bar()
while True:
next_bar = Bar()
bar.next_bar = next_bar
bar = next_bar
return initial_bar
The intent being that a chain of Bar
s is formed which can be followed, linked-list style.
This was all very well; but through some misguided notion I wanted to cut it down by a line, compounding the assignments at the end of the loop into a single line.
def foo():
bar = initial_bar = Bar()
while True:
next_bar = Bar()
bar = bar.next_bar = next_bar
return initial_bar
Because bar = bar.next_bar = next_bar
will expand to bar.next_bar = next_bar
followed by effectively bar = bar.next_bar
. (Except that it doesn't.)
The problem is, this doesn't work; the "initial bar" returned does not have its next_bar
defined. I can easily enough work around it by going back to the more explicit two-line solution, but what's going on?
It's time to pull out
dis
.If you look closely at that, you'll see that in the critical line (line 5, see the numbers on the left, positions 31-47), it does this:
next_bar
(31) twice (34);bar
(35);bar.next_bar
(38, 41).This is seen more obviously in a minimal test case.
See what it's doing. This means that
b = c = d
is actually equivalent tob = d; c = d
. Normally this won't matter, but in the case mentioned originally, it does matter. It means that in the critical line,is not equivalent to
But rather to
This is, in fact, documented, in section 6.2 of the Python documentation, Simple statements, Assignment statements:
There is also a related warning in that section which applies to this case:
It's possible to go for
bar.next_bar = bar = next_bar
and that does produce the initially desired result, but have pity on anyone (including the original author some time later!) who will have to read the code later and rejoice in the fact that, in words that I'm sure Tim would have used had he thought of them,