1

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?

Root
  • 97
  • 1
  • 12
  • 1
    May we see the implementation of your list (especially `next`)? In theory, `next` should be internal and shouldn't be played with from outside the class. – CristiFati Sep 04 '17 at 17:26
  • It's from this website. [link](https://leetcode.com/problems/reverse-linked-list-ii/description/). And the next function is listed in the code area, but I am not sure if you can see it or not. I'll update a pic of it. – Root Sep 04 '17 at 17:41

2 Answers2

2

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
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
1

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]
MSeifert
  • 145,886
  • 38
  • 333
  • 352
  • Because it doesn't matter if `a = tmp[0]` and `b=tmp[1]` (when you use `a, b = b, a+b`) or `b = tmp[0]` and `a=tmp[1]` (in case of `b, a = a+b, b`). What matters is that the right hand side is evaluated *completely* before it's unpacked into the left hand side (but each side is evaluated left-to-right). I mean, you could even use `a, a = a+b, a` -> then the resulting `a` would just be the original `a` because that's the "last" unpacked assignment to `a` (contrary to `a, a = a, a+b` where the resulting `a` is equal to `a+b` because that's "unpacked last"). – MSeifert Sep 04 '17 at 22:50
  • I see your point. The right-hand side elements calculate first, and then unpackage to left-hand side elements one by one from left to right. – Root Sep 05 '17 at 01:07