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?

share
    
It's different because instead of assigning an alias, it copies the list. – zondo 16 hours ago
    
3  
Omitting the comma gives an error message to which this might be an interesting reference: "SyntaxError: starred assignment target must be in a list or tuple". – Josh Lee 16 hours ago
up vote 27 down vote accepted

*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
share
6  
Also worth noting that the comma actually belongs to the *x, as the starred assignment must be in a list or tuple. So the more explicit way to write that statement is (*x,) = p – glibdud 16 hours ago
1  
Important to note, this works when p is any iterable. – juanpa.arrivillaga 16 hours ago
    
Might be worth adding something like x[0] = 3; p #=> [1, 2] to the first half and x[0] = 3; p #=> [3, 2] to the second, to illustrate why is and == are different – QPaysTaxes 10 hours ago

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 "Least Astonishment" and the Mutable Default Argument)

Two other common ways of producing this effect are:

>>> x = list(p)

and

>>> x = p[:]

The second 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.

share
    
Note, though, that your second bit of code, x = p[:] requires that p be slice-able. This excludes things like generators. – nneonneo 16 hours ago
    
@nneonneo: Good point. I've edited now. – zondo 16 hours ago
1  
Wait, 3.0!? I thought it was way more recent, 3.5 or 3.6 or something. – user2357112 14 hours ago
2  
@user2357112: In What's New In Python 3.0, you will find this bullet point: PEP 3132 was accepted. In addition, the PEP itself says the Python version is 3.0. I'm pretty sure that must be the case, then. – zondo 14 hours ago

You should always throw these to dis and see what it tells you, you'll see how *x, = p is quite 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 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 difference between these; it's documented as doing:

Implements assignment with a starred target: Unpacks an iterable in TOS 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 you get a new object that's referred to with the name x and not a reference to an already existing object (as is the case with x = p) as Eugene noted.

share

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.