了解* x,= lst

我正在浏览一些旧代码,试图理解它的作用,并且我遇到了这个奇怪的声明:

*x ,= p

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中引入的功能(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只是表示xp另一个名称。 然而,在这种情况下,这是一个刚好在其中具有相同值的新列表。 (您可能对“最小惊讶”和可变默认参数感兴趣)

产生这种效应的另外两种常见方式是:

>>> x = list(p)

>>> x = p[:]

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

x = p.copy()

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


你应该总是把这些给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(堆栈顶部)中的iterable解包为单个值,其中值的总数可以小于迭代中的项的数量:其中一个新值将成为all剩余物品。

正如Eugene指出的那样,这就是为什么你得到一个名称为x的新对象,而不是对已经存在的对象的引用(就像x = p的情况一样)。


*x,看起来很奇怪(这里和所有额外的逗号),但它在这里是必需的。 左手边必须是一个元组或列表,并且由于在Python中创建一个单一的元素元组的诡诈,你需要使用一个尾随,

i = 1, # one element tuple

如果你喜欢迷惑人,你可以使用这个list版本:

[*x] = p

它完全一样的东西,但没有额外的逗号挂在那里。

链接地址: http://www.djcxy.com/p/18801.html

上一篇: Understanding *x ,= lst

下一篇: Iterating vs List Concatenation