Understanding *x ,= lst
PythonPython 3.xIterable UnpackingPython Problem Overview
I'm going through some old code trying to understand what it does, and I came across this odd statement:
*x ,= p
p
is a list in this context. I've been trying to figure out what this statement does. As far as I can tell, it just sets x
to the value of p
. For example:
p = [1,2]
*x ,= p
print(x)
Just gives
[1, 2]
So is this any different than x = p
? Any idea what this syntax is doing?
Python Solutions
Solution 1 - Python
*x ,= p
is basically an obfuscated version of x = list(p)
using extended iterable unpacking. The comma after x
is required to make the assignment target a tuple (it could also be a list though).
*x, = p
is different from x = p
because the former creates a copy of p
(i.e. a new list) while the latter creates a reference to the original list. To illustrate:
>>> p = [1, 2]
>>> *x, = p
>>> x == p
True
>>> x is p
False
>>> x = p
>>> x == p
True
>>> x is p
True
Solution 2 - Python
It's a feature that was introduced in Python 3.0 (PEP 3132). In Python 2, you could do something like this:
>>> p = [1, 2, 3]
>>> q, r, s = p
>>> q
1
>>> r
2
>>> s
3
Python 3 extended this so that one variable could hold multiple values:
>>> p = [1, 2, 3]
>>> q, *r = p
>>> q
1
>>> r
[2, 3]
This, therefore, is what is being used here. Instead of two variables to hold three values, however, it is just one variable that takes each value in the list. This is different from x = p
because x = p
just means that x
is another name for p
. In this case, however, it is a new list that just happens to have the same values in it. (You may be interested in https://stackoverflow.com/q/1132941/5827958)
Two other common ways of producing this effect are:
>>> x = list(p)
and
>>> x = p[:]
Since Python 3.3, the list object actually has a method intended for copying:
x = p.copy()
The slice is actually a very similar concept. As nneonneo pointed out, however, that works only with objects such as lists and tuples that support slices. The method you mention, however, works with any iterable: dictionaries, sets, generators, etc.
Solution 3 - Python
You should always throw these to dis
and see what it throws back at you; you'll see how *x, = p
is actually different from x = p
:
dis('*x, = p')
1 0 LOAD_NAME 0 (p)
2 UNPACK_EX 0
4 STORE_NAME 1 (x)
While, the simple assignment statement:
dis('x = p')
1 0 LOAD_NAME 0 (p)
2 STORE_NAME 1 (x)
(Stripping off unrelated None
returns)
As you can see UNPACK_EX
is the different op-code between these; it's documented as:
> Implements assignment with a starred target: Unpacks an iterable in TOS (top of stack) into individual values, where the total number of values can be smaller than the number of items in the iterable: one of the new values will be a list of all leftover items.
Which is why, as Eugene noted, you get a new object that's referred to by the name x
and not a reference to an already existing object (as is the case with x = p
).
*x,
does seem very odd (the extra comma there and all) but it is required here. The left hand side must either be a tuple or a list and, due to the quirkiness of creating a single element tuple in Python, you need to use a trailing ,
:
i = 1, # one element tuple
If you like confusing people, you can always use the list
version of this:
[*x] = p
which does exactly the same thing but doesn't have that extra comma hanging around there.
Solution 4 - Python
You can clearly understand it from below example
L = [1, 2, 3, 4]
while L:
temp, *L = L
print(temp, L)
what it does is, the front variable will get the first item every time and the remaining list will be given to L.
The output will look shown below.
1 [2, 3, 4]
2 [3, 4]
3 [4]
4 []
Also look at below example
x, *y, z = "python"
print(x,y,z)
In this both x,z will get each one letter from the string meaning first letter is assigned to x and the last letter will be assigned to z and the remaining string will be assigned to variable y.
p ['y', 't', 'h', 'o'] n
One more example,
a, b, *c = [0,1,2,3]
print(a,b,c)
0 1 [2,3]
Boundary case: If there is nothing remaining for star variable then it will get an empty list.
Example:
a,b=[1]
print(a,b)
1 []