我正在阅读一些旧代码,试图了解它的作用,我偶然发现了这个奇怪的声明:
*x ,= p
p
是此上下文中的列表。我一直试图找出这句话的作用。据我所知,它只是将x
设置为p
的值。例如:
p = [1,2]
*x ,= p
print(x)
刚刚给出
[1, 2]
这与x = p
有什么不同?知道这个语法在做什么吗?
答案 0 :(得分:72)
*x ,= p
基本上是使用extended iterable unpacking的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
答案 1 :(得分:44)
它是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
的另一个名称。但是,在这种情况下,它是一个新列表,恰好在其中具有相同的值。 (您可能对"Least Astonishment" and the Mutable Default Argument)
产生这种效果的另外两种常见方法是:
>>> x = list(p)
和
>>> x = p[:]
从Python 3.3开始,list对象实际上有一个用于复制的方法:
x = p.copy()
切片实际上是一个非常相似的概念。然而,正如nneonneo指出的那样,它仅适用于支持切片的列表和元组等对象。但是,您提到的方法适用于任何可迭代的:字典,集合,生成器等。
答案 2 :(得分: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
是这些之间的不同操作码; it's documented as:
使用已加星标的目标实现赋值:将TOS(堆栈顶部)中的iterable解包为单个值,其中值的总数可以小于iterable中的项数:其中一个新值将是所有剩余物品的清单。
正如Eugene所说,这就是为什么你得到一个名为x
的新对象,而不是对已存在对象的引用(如x = p
的情况)。
*x,
看起来很奇怪(那里有额外的逗号和所有)但这里需要它。左侧必须是元组或列表,并且由于在Python中创建单个元素元组的怪癖,您需要使用尾随,
:
i = 1, # one element tuple
如果您喜欢令人困惑的人,您可以随时使用list
版本:
[*x] = p
完全相同,但没有那个额外的逗号挂在那里。
答案 3 :(得分:0)
您可以从下面的示例中清楚地了解它
L = [1, 2, 3, 4]
while L:
temp, *L = L
print(temp, L)
它的作用是,前面的变量每次都会获得第一项,其余的列表将被赋予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 []