理解 *x ,= lst

Understanding *x ,= lst

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

*x ,= p

p 是此上下文中的列表。我一直在试图弄清楚这个声明的作用。据我所知,它只是将 x 设置为 p 的值。例如:

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

刚给

[1, 2]

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

*x ,= p 基本上是使用 extended iterable unpackingx = list(p) 的混淆版本。 x 之后的逗号是使赋值目标成为一个元组所必需的(尽管它也可以是一个列表)。

*x, = p 不同于x = p因为前者创建了pcopy (即一个新列表)而后者创建一个 reference 到原始列表。举例说明:

>>> 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 的另一个名称。然而,在这种情况下,它是一个新列表,其中恰好具有相同的值。 (您可能对"Least Astonishment" and the Mutable Default Argument感兴趣)

另外两种产生这种效果的常见方法是:

>>> x = list(p)

>>> x = p[:]

从Python 3.3开始,列表对象实际上有一个用于复制的方法:

x = p.copy()

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

你应该总是把这些扔给 dis 看看它会把什么扔给你;您会看到 *x, = px = 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)

(剥离无关Nonereturns)

如您所见,UNPACK_EX 是它们之间的不同操作码; 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.

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


*x, 确实看起来很奇怪(那里多余的逗号)但这里是必需的。左侧必须是元组或列表,并且由于在 Python 中创建单个元素元组的古怪之处,您需要使用尾随 ,:

i = 1, # one element tuple

如果你喜欢混淆别人,你可以随时使用这个的 list 版本:

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

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

示例:

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

1 []