Why is the result different if I switch the sequence in setting variables by common?

96 views Asked by At

To explain better, I wrote a small class node, and set a and b:

class node(object):
    def __init__(self,x,y):
        self.val = x 
        self.next = y

a = node(5,6)
b = None

Then I found the result is different between:

a, a.next, b = a.next, b, a
print(a,b)  #it returns AttributeError: 'int' object has no attribute 'next'

and:

a.next, a, b = b, a.next, a
print(a,b)  #it returns 6 <__main__.node object at 0x1021a0400>

We all know, when a, b = b, a+b, it gives a,b value simultaneously, and the result doesn't change when the code becomes b, a = a+b, b.

So, could anybody help me with it? Why does this happen?

2

There are 2 answers

2
MSeifert On BEST ANSWER

The unpacking isn't simultaneously. It's just that the right hand side is "constructed" before it's "unpacked" into the left hand side. But each side is evaluated left-to-right!

So what happens is roughly like this (it actually doesn't build the tuple but that's just an implementation detail):

tmp = (a.next, b, a)
a = tmp[0]
a.next = tmp[1]  # fails because "a" is now an integer
b = tmp[2]

In the second case it works because is "re-assigned" after "re-assigning" a.next:

tmp = (b, a.next, a)
a.next = tmp[0]  # a still has the next attribute
a = tmp[1]
b = tmp[2]
0
Ignacio Vazquez-Abrams On

Tuple unpacking only happens "simultaneously" when the LHS elements are independent. Since you have created a situation where they are not independent, minor details in the implementation now matter.

In this case, the detail that matters is that the LHS elements are assigned to from left to right. Since a contains an int by the time a.next is looked at, an exception is thrown.

import dis

def f1():
    a, a.next, b = a.next, b, a

def f2():
    a.next, a, b = b, a.next, a

print('f1:')
dis.dis(f1)
print()
print('f2:')
dis.dis(f2)

...

f1:
  4           0 LOAD_FAST                0 (a)
              3 LOAD_ATTR                0 (next)
              6 LOAD_FAST                1 (b)
              9 LOAD_FAST                0 (a)
             12 ROT_THREE
             13 ROT_TWO
             14 STORE_FAST               0 (a)
             17 LOAD_FAST                0 (a)
             20 STORE_ATTR               0 (next)
             23 STORE_FAST               1 (b)
             26 LOAD_CONST               0 (None)
             29 RETURN_VALUE

f2:
  7           0 LOAD_FAST                0 (b)
              3 LOAD_FAST                1 (a)
              6 LOAD_ATTR                0 (next)
              9 LOAD_FAST                1 (a)
             12 ROT_THREE
             13 ROT_TWO
             14 LOAD_FAST                1 (a)
             17 STORE_ATTR               0 (next)
             20 STORE_FAST               1 (a)
             23 STORE_FAST               0 (b)
             26 LOAD_CONST               0 (None)
             29 RETURN_VALUE