Can anybody explain what's going on here? Why does this happen?
>>> b = "1984"
>>> a = b, c = "AB"
>>> print(a, b, c)
'AB', 'A', 'B'
This behavior really blows my mind. Found this here
Can anybody explain what's going on here? Why does this happen?
>>> b = "1984"
>>> a = b, c = "AB"
>>> print(a, b, c)
'AB', 'A', 'B'
This behavior really blows my mind. Found this here
Assignment is a statement; it's defined to assign the far right side to the various targets from left to right. The rather dry language grammar description is:
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.
So for example:
a = b = 1
assigns 1
to a
, then assigns it again to b
, roughly the same as if you did:
__specialtmp = 1
a = __specialtmp
b = __specialtmp
where __specialtmp
is an unnamed temporary storage location (on CPython, it's just loaded on the top of the program stack, then duplicated into two references, then each reference is popped off for assignment).
This just adds iterable unpacking to the mix; expanding your code the same way, it would look like:
__specialtmp = "AB"
a = __specialtmp # Assigns original value to a
b, c = __specialtmp # Unpacks string as iterable of its characters, assigning "A" to b, and "B" to c
This won't always work mind you; if the thing being unpacked is an iterator, and you assign to the unpacked names first, the iterator will be exhausted and nothing useful will be available for the second assignment:
b, c = [*a] = iter("AB")
This unpacks "A"
to b
, and "B"
to c
, but when it gets to a
, which in plain [*a] = iter("AB")
would become ["A", "B"]
(the star syntax capturing "remaining" values to a list
), in this case, the iterator gets exhausted populating b
and c
and a
gets nothing (the empty list
, []
).
Point is, while this trick works, I wouldn't recommend it in general. Initializing multiple names to the same immutable value is fine, but it's liable to bite you otherwise.
Such a cool question! Makes a lot of fun! :) Can be used at interviews :)
Ok, here we are
>>> b = "1984"
>>> a = b, c = "AB"
>>> print((a,b,c))
('AB', 'A', 'B')
>>> a = (b, c) = "AB"
>>> print((a,b,c))
('AB', 'A', 'B')
>>>
In python for multiple assignments, you can omit (...)
and it looks like python parses this line similar to 2 lines
a = "AB"
b, c = "AB" # which is equal to (b, c) = "AB"
Some more examples
>>> a = b, c = "AB"
>>> print((a,b,c))
('AB', 'A', 'B')
>>> a = (b, c) = "AB"
>>> print((a,b,c))
('AB', 'A', 'B')
>>> a = "AB"
>>> b, c = "AB"
>>> print((a,b,c))
('AB', 'A', 'B')
>>>
It works using lists a well :)
>>> a = [b, c] = 'AB'
>>> print((a,b,c))
('AB', 'A', 'B')
>>>
Some more examples:
Let's make this a little simpler. Let's look at the following case
>>> b = 1
>>> b, c = (0, 2)
>>> print(b, c)
0, 2
Is it surprising that b
is 0 and not 1? It shouldn't be since we assigning b
to 0 and c
to 2 when calling b, c = (0, 2)
thanks to tuple unpacking.
Now to address the other part of the gotcha, let's take this example
>>> b = 1
>>> a = b = 0
>>> print (b)
0
Is it again surprising that b is 0 and not 1? Again, it shouldn't be since when calling a = b = 0
, we've assigned both a
and b
to 0 with multiple assignment.
So coming back to the gotcha, the a = b, c = "AB"
is just a combination of these two behaviors. b, c = "AB"
will unpack "A"
to b
and "B"
to c
, and we're also assigning "AB"
to a
. While it looks like we're assigning a = b
, we're really just doing the following two lines
>>> b = "1984"
>>> b, c = "AB"
>>> a = "AB"
Hopefully this breaks down where the tuple unpacking is happening and where the assignment is happening, and that it's not as confusing of a gotcha as it might look.