Python 理解*x,=lst

Python 理解*x,=lst,python,python-3.x,iterable-unpacking,Python,Python 3.x,Iterable Unpacking,我正在浏览一些旧代码,试图理解它的功能,我遇到了一个奇怪的语句: *x ,= p dis('x = p') 1 0 LOAD_NAME 0 (p) 2 STORE_NAME 1 (x) p是此上下文中的一个列表。我一直在想这句话是怎么说的。据我所知,它只是将x的值设置为p。例如: p = [1,2] *x ,= p print(x) 只给 [1, 2] 这和x=p有什么不

我正在浏览一些旧代码,试图理解它的功能,我遇到了一个奇怪的语句:

*x ,= p
dis('x = p')
  1           0 LOAD_NAME                0 (p)
              2 STORE_NAME               1 (x)
p
是此上下文中的一个列表。我一直在想这句话是怎么说的。据我所知,它只是将
x
的值设置为
p
。例如:

p = [1,2]
*x ,= p    
print(x)
只给

[1, 2]

这和
x=p
有什么不同吗?知道这个语法在做什么吗?

*x,=p
基本上是
x=list(p)
的模糊版本。
x
后面的逗号是使赋值目标成为元组所必需的(但也可以是列表)

*x,=p
x=p
不同,因为前者创建了
p
的副本(即新列表),而后者创建了对原始列表的引用。举例说明:

>>> p = [1, 2]
>>> *x, = p 
>>> x == p
True
>>> x is p
False
>>> x = p
>>> x == p
True
>>> x is p
True

这是Python 3.0()中引入的一个特性。在Python 2中,您可以执行如下操作:

>>> p = [1, 2, 3]
>>> q, r, s = p
>>> q
1
>>> r
2
>>> s
3
Python 3对此进行了扩展,以便一个变量可以容纳多个值:

>>> p = [1, 2, 3]
>>> q, *r = p
>>> q
1
>>> r
[2, 3]
因此,这里使用的就是这个。但是,不是用两个变量来保存三个值,而是用一个变量来获取列表中的每个值。这与
x=p
不同,因为
x=p
只是意味着
x
p
的另一个名称。然而,在本例中,它是一个新列表,其中恰好有相同的值。(您可能对以下内容感兴趣)

产生这种效果的其他两种常见方法是:

>>> x = list(p)

自Python 3.3以来,list对象实际上有一个用于复制的方法:

x = p.copy()

切片实际上是一个非常相似的概念。然而,正如nneonneo所指出的,这只适用于支持切片的对象,如列表和元组。但是,您提到的方法适用于任何iterable:字典、集合、生成器等。

您应该始终将这些元素扔给,看看它会给您带来什么;您将看到
*x,=p
实际上与
x=p
有什么不同:

dis('*x, = p')
  1           0 LOAD_NAME                0 (p)
              2 UNPACK_EX                0
              4 STORE_NAME               1 (x)
而简单赋值语句:

*x ,= p
dis('x = p')
  1           0 LOAD_NAME                0 (p)
              2 STORE_NAME               1 (x)
(剥离不相关的
None
returns)

正如您所看到的,解包是不同的操作码;:

使用星号目标实现赋值:将TOS(堆栈顶部)中的iterable解压为单个值,其中值的总数可以小于iterable中的项数:其中一个新值将是所有剩余项的列表

这就是为什么,正如Eugene所指出的,您会得到一个名为
x
的新对象,而不是对已经存在的对象的引用(如
x=p


*x,
看起来确实很奇怪(这里还有多余的逗号),但这里需要它。左侧必须是元组或列表,并且由于在Python中创建单个元素元组的特殊性,您需要使用尾部的

i = 1, # one element tuple
如果您喜欢混淆他人,您可以始终使用此列表的
版本:

[*x] = p

它的作用完全相同,但没有多余的逗号。

您可以从下面的示例中清楚地理解它

L = [1, 2, 3, 4]
while L:
    temp, *L = L             
    print(temp, L)
它所做的是,front变量每次都会获取第一项,剩余的列表将被提供给L

输出如下所示

1 [2, 3, 4]
2 [3, 4]
3 [4]
4 []
再看看下面的例子

x, *y, z = "python"
print(x,y,z)
在这种情况下,x、z都将从字符串中获取每个字母,这意味着第一个字母将分配给x,最后一个字母将分配给z,其余字符串将分配给变量y

p ['y', 't', 'h', 'o'] n
再举一个例子,

a, b, *c = [0,1,2,3]
print(a,b,c)

0 1 [2,3]
边界情况:如果星型变量没有剩余值,那么它将得到一个空列表。

例如:

a,b=[1]
print(a,b)

1 []

这是不同的,因为它不是分配别名,而是复制列表。请参阅,省略逗号会给出一条错误消息,这可能是一个有趣的参考:“SyntaxError:带星号的分配目标必须位于列表或元组中”。还值得注意的是,逗号实际上属于
*x
,,因为带星号的赋值必须在列表或元组中。因此,编写该语句的更明确的方法是
(*x,)=p
,需要注意的是,当
p
是任意可数时,该方法有效;p#=>[1,2]
到上半部分和
x[0]=3;p#=>[3,2]
到第二位,来说明为什么
=
是不同的。注意,您的第二位代码,
x=p[:]
要求
p
是可切片的。这不包括发电机之类的东西。等等,3.0!?我认为它是更新的版本,3.5或3.6或其他版本。@user2357112:In,你会发现这个要点:
pep3132已被接受
。此外,政治公众人物本身说Python版本是3.0。我很确定情况一定是这样的。@zondo在Python3中,复制的切片方法有一个问题,您可以将其包括在内,因为这个问题只适用于Python3。(虽然只有Python 3.3及以上版本有。)@River:啊,是的。我已经忘记了那件事。我保留了这个片段,因为它不仅适用于列表,而且仍然是一种常见的策略,但我添加了
.copy
方法。