Understanding *x ,= lst

喜夏-厌秋 提交于 2019-12-03 02:31:21

问题


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?


回答1:


*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



回答2:


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[:]

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.




回答3:


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.



来源:https://stackoverflow.com/questions/43190992/understanding-x-lst

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!