Reputation: 3415
Below, I illustrate with three examples what I mean with "swapping" and "sequential execution of assignment statements".
Tuple assignment can be very handy in order to swap the contents of variables.
The following example shows how we can swap the contents of two elements in an array in a clear an concise way without the need of temporary variables:
a = [1,2]
# prints [1, 2]
print(a)
a[0], a[1] = a[1], a[0]
# prints: [2,1] and not: [2, 2] as with a sequential assignment!
print(a)
The example shows us how the tuple assignment behaves like a swap, instead of a sequential execution of doing the first assignment, and then doing the third assignment.
Here's another example that swaps three integers:
x, y, z = 1, 2, 3
# prints: 1 2 3
print(x, y, z)
# swap contents in variables:
x, y, z = z, y, x
# prints: 3 2 1 and not: 3 2 3 as with a sequential assignment!
print(x, y, z)
However, once things get more complicated than simple datatypes, the tuple assignment may also behave like a sequential assignment.
Let's consider the following linked list implementation:
class ListNode:
def __init__(self, data=None, next=None):
self.data = data
self.next = next
def __repr__(self):
is_first = True
current = self
l = []
safety_count = 0
while current and safety_count < 10:
if not is_first:
l.append(' -> ')
is_first = False
l.append(str(current.data))
current = current.next
safety_count += 1
return ''.join(l)
def __str__(self):
return self.__repr__()
This function reverses the order of the linked list (and works perfectly fine):
def reverse_list_working(L):
if not L:
return L
pre_current = None
current = L
while True:
tmp_next = current.next
current.next = pre_current
pre_current = current
current = tmp_next
if not current:
break
return pre_current
Now, we might be tempted to get rid of the tmp_ variable through a tuple assignment, to have a swap of the variable's contents:
def reverse_list_not_working(L):
pre_current = None
current = L
while True:
pre_current, current, current.next = current, current.next, pre_current
if not current:
break
return pre_current
However, this implementations gives an error, once we get to the last element. The problem here is that the tuple assignment behaves like a sequential assignment.
Upvotes: 4
Views: 553
Reputation: 3415
Here's another answer that summarizes in pseudo-code how the tuple assignment works:
The following tuple-assignment:
a, b, c = e1, e2, e3
Is translated into:
e1_ = eval(e1)
e2_ = eval(e2)
e3_ = eval(e3)
a = e1_
b = e2_
c = e3_
So, note that the evaluation of expression e1
might have effects on the evaluation of the expression e2
if e1
changes some shared state that e2
accesses.
Similarly, an assignment to a
might affect the assignment of b
, if b
is dependant on a (e.g., a = c
, b = c.next
).
Hence, the tuple-assignment is not just a "swap".
class Num:
def __init__(self, val):
self.val = val
def __add__(self, other):
return Num(self.val + other)
def __str__(self):
return str(self.val)
class Counter:
def __init__(self):
self.val = Num(1)
def get_and_increment(self):
return_val = self.val
self.val += 1
return return_val
c = Counter()
a = Num(1) a.val = Num(2) b = a
a.val, b.val = c.get_and_increment(), c.get_and_increment()
print(str(a)) # -> prints 2 print(str(b)) # -> prints 2
Upvotes: 1
Reputation: 9679
If you take the simple example (three integers) and look at the byte code, you see:
...
LOAD_NAME 2 (z)
LOAD_NAME 1 (y)
LOAD_NAME 0 (x)
ROT_THREE
ROT_TWO
STORE_NAME 0 (x)
STORE_NAME 1 (y)
STORE_NAME 2 (z)
...
I.e. the answer is you first get values for the right hand side onto stack (simple case just get the values referred to by variable, but in complex case, that can have broader impact), then you reorder, and then you assign values from stack to corresponding variable on the left hand side.
Upvotes: 0