了解* x,= lst


72

我正在阅读一些旧代码,试图了解它的作用,并且遇到了这个奇怪的陈述:

*x ,= p

p在这种情况下是一个列表。我一直在努力弄清楚该语句的作用。据我所知,它只是设置x为的值p。例如:

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

只是给

[1, 2]

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


这是不同的,因为它没有分配别名,而是复制列表。
zondo


4
省略逗号会给出一条错误消息,该消息可能是一个有趣的参考:“ SyntaxError:已加星标的分配目标必须在列表或元组中”。
Josh Lee

Answers:


76

*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

14
同样值得注意的是,逗号实际上属于*x,因为加星标的赋值必须在列表或元组中。因此,编写该语句的更明确的方法是(*x,) = p
glibdud

2
需要注意的,当这个作品p任何可迭代。
juanpa.arrivillaga

1
可能是值得添加类似x[0] = 3; p #=> [1, 2]的上半年x[0] = 3; p #=> [3, 2]到第二,说明为什么is==是不同的
基金莫妮卡的诉讼

47

这是Python 3.0(PEP 3132)中引入的功能。在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)

>>> x = p[:]

从Python 3.3开始,list对象实际上具有用于复制的方法:

x = p.copy()

切片实际上是一个非常相似的概念。正如nneonneo指出的那样,这仅适用于支持切片的列表和元组之类的对象。但是,您提到的方法适用于任何可迭代的方法:字典,集合,生成器等。


但是请注意,您的第二段代码x = p[:]要求它p是可切片的。这不包括发电机。
nneonneo

1
等等3.0 !? 我认为是最近的版本,3.5或3.6左右。
user2357112支持Monica's

2
@ user2357112:在Python 3.0的新增功能中,您将找到以下要点:PEP 3132 was accepted。另外,PEP本身说Python版本是3.0。我很确定那是必须的。
zondo

@zondo在Python3中,复制的切片方法有一个新的可读性更高的副本,您可能会添加它,因为此问题仅适用于Python3。(尽管只有Python 3.3及更高版本具有此功能。)
River

1
@河:嗯,是的。我已经忘记了那个。我保留切片是因为它不仅对列表有用,而且仍然是常见的战术,但是我添加了该.copy方法。
zondo

14

您应该始终将这些扔给您dis,看看它会把什么扔回到您身上;您会看到与以下*x, = p内容实际上有何不同x = p

dis('*x, = p')
  1           0 LOAD_NAME                0 (p)
              2 UNPACK_EX                0
              4 STORE_NAME               1 (x)

而,简单的赋值语句:

dis('x = p')
  1           0 LOAD_NAME                0 (p)
              2 STORE_NAME               1 (x)

(剥离无关的None回报)

如您所见UNPACK_EX,它们之间是不同的操作码。它记录为

实现带有加星标目标的分配:将TOS(堆栈顶部)中的可迭代项解压缩为单个值,其中值的总数可以小于可迭代项中的项数:新值之一将是所有值的列表剩余物品。

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


*x,确实看起来很奇怪(所有地方都有多余的逗号),但这是必需的。左侧必须是一个元组或一个列表,并且由于在Python中创建单个元素元组的古怪性,您需要使用结尾,

i = 1, # one element tuple

如果您喜欢使人困惑,则可以始终使用以下list版本:

[*x] = p

它做的完全一样,但是没有多余的逗号在那儿。


2

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

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]

边界情况:如果star变量没有剩余,那么它将得到一个空列表。

例:

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

1 []
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.