Python将多个变量分配给相同的值?列出行为


131

我试图使用如下所示的多重赋值来初始化变量,但是我对此行为感到困惑,我希望分别重新赋值列表,我的意思是b [0]和c [0]等于0。

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

结果是:[1、3、5] [1、3、5] [1、3、5]

那是对的吗?多重分配应该使用什么?有什么不同呢?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

结果:('f:',3)('e:',4)


2
你想abc,所有指向相同的值(在这种情况下的列表),还是你想a=0b=3c=5。在这种情况下,您只想要a,b,c = [0,3,5]a,b,c = 0,3,5
13年

Answers:


271

如果您要使用C / Java / etc等语言的Python。家庭,这可能会帮助您停止将其a视为“变量”,而开始将其视为“名称”。

a,,bc不是具有相等值的不同变量;它们是相同名称的不同名称。变量具有类型,身份,地址和类似的东西。

名称没有任何名称。当然可以,并且对于相同的值,您可以有很多名称。

如果给Notorious B.I.G.热狗* Biggie Smalls,请Chris Wallace带一个热狗。如果将的第一个元素更改a为1,则b和的第一个元素为c1。

如果您想知道两个名称是否在命名同一对象,请使用is运算符:

>>> a=b=c=[0,3,5]
>>> a is b
True

然后您问:

有什么不同呢?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

在这里,您将名称重新绑定e到value 4。这并不影响名称df以任何方式。

在先前的版本中,您分配给a[0],而不是a。因此,从的角度来看a[0],您正在重新绑定a[0],但是从的角度来看a,您正在就位更改它。

您可以使用该id函数,该函数为您提供代表对象身份的唯一编号,以准确查看哪个对象是哪个对象,即使在is无济于事的情况下:

>>> a=b=c=[0,3,5]
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261120
>>> id(b[0])
4297261120

>>> a[0] = 1
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261216
>>> id(b[0])
4297261216

请注意,该a[0]名称已从4297261120更改为4297261216-现在它是另一个值的名称。并且b[0]现在也是该新值的名称。这是因为ab仍在命名同一对象。


在幕后,a[0]=1实际上是在列表对象上调用方法。(等效于a.__setitem__(0, 1)。)因此,它实际上根本没有重新绑定任何内容。这就像打电话my_object.set_something(1)。当然,该对象可能是重新绑定实例属性以实现此方法,但这并不重要。重要的是您没有分配任何东西,只是在改变对象。与相同a[0]=1


user570826询问:

如果有的话 a = b = c = 10

这与以下情况完全相同a = b = c = [1, 2, 3]:您有三个名称相同的值。

但是在这种情况下,值是an int,而ints是不可变的。在这两种情况下,你可以重新绑定a到一个不同的值(例如,a = "Now I'm a string!"),但不会影响原来的值,这bc将仍然是名称。不同的是,有一个列表,你可以更改值[1, 2, 3][1, 2, 3, 4]这样做,例如a.append(4); 由于那实际上是在更改值bc的名称,b因此现在b [1, 2, 3, 4]。无法将值更改10为其他任何值。10永远是10,就像克劳迪娅(Claudia)一样,吸血鬼也永远是5(至少直到她被Kirsten Dunst取代)。


*警告:请勿给臭名昭著的BIG热狗。黑帮说唱僵尸永远不要在午夜之后喂饱。


如果我们have, a = b = c = 10;以及当我们尝试更新b的值时会影响其他值怎么办?虽然我检查了他们的身份证是一样的。
2014年

@ user570826:10是不可变的,这意味着无法更新该值,因此您的问题毫无意义。您可以指向b其他值,但是这样做对ac仍然没有作用,它们仍然指向原始值。列表的区别在于它们是可变的,例如,您可以append对列表或进行更改lst[0] = 3,这将更新值,该值将在该值的所有名称中可见。
abarnert 2014年

72

咳嗽

>>> a,b,c = (1,2,3)
>>> a
1
>>> b
2
>>> c
3
>>> a,b,c = ({'test':'a'},{'test':'b'},{'test':'c'})
>>> a
{'test': 'a'}
>>> b
{'test': 'b'}
>>> c
{'test': 'c'}
>>> 

10
恕我直言,这实际上回答了OP的首要问题,即我应该对多重分配使用什么,而上面给出的评分更高,头脑更聪明的答案则没有。
Will Croxford

2
或者a,b,c = 1,2,3,如果您确实想要额外的cm可读性,那么在Python 2或3中也可以不使用方括号。
克罗克斯福德

14

是的,这是预期的行为。a,b和c都设置为同一列表的标签。如果需要三个不同的列表,则需要分别分配它们。您可以重复显示列表,也可以使用多种方式之一复制列表:

b = a[:] # this does a shallow copy, which is good enough for this case
import copy
c = copy.deepcopy(a) # this does a deep copy, which matters if the list contains mutable objects

Python中的赋值语句不复制对象-它们将名称绑定到对象,并且对象可以具有与您设置的一样多的标签。在第一次编辑中,更改a [0],您将更新a,b和c均引用的单个列表中的一个元素。在第二次更改e中,您将e切换为另一个对象的标签(4而不是3)。


13

在python中,一切都是对象,也是“简单”的变量类型(int,float等)。

当您更改变量值时,实际上是在更改其指针,如果在两个变量之间进行比较,则会对其指针进行比较。(要清楚,指针是物理计算机内存中存储变量的地址)。

结果,当您更改内部变量值时,会更改其在内存中的值,并且会影响指向该地址的所有变量。

例如,当您这样做时:

a = b =  5 

这意味着a和b指向内存中包含值5的相同地址,但是当您这样做时:

a = 6

它不会影响b,因为a现在指向另一个包含6的存储位置,而b仍然指向包含5的存储地址。

但是,当您这样做时:

a = b = [1,2,3]

a和b再次指向相同的位置,但是不同之处在于,如果更改列表值之一:

a[0] = 2

它会更改a所指向的内存的值,但是a仍然指向与b相同的地址,结果b也将更改。


6
这是极具误导性的。指针在Python级别当然是不可见的,并且四个主要实现中的至少两个(PyPy和Jython)甚至在实现内部也没有使用它们。
13年

1
欢迎阅读和探索python内部结构,并且会发现python中的每个变量实际上都是指针。
Ori Seri

4
不可以。在Python(CPython)的一种实现中,每个变量都是指向的指针PyObject。在其他实现中(例如PyPy或Jython)则不是这样。(事实上,它甚至不清楚它如何可能是真的,因为这些实现都写在语言甚至没有指针。)
abarnert

我认为在概念上使用“指针”是可以的(也许免责声明实现可能会有所不同),尤其是目标是传达行为时。
莱文

@abarnert当人们说Python时,它们的意思是CPython,而不是其他很少使用的实现。就像人们说面巾纸一样,它们的意思是面巾纸。在这些注释中玩语义游戏确实是不必要的。至于他写的东西,他所描述的行为是错误的吗?
swade

10

您可以id(name)用来检查两个名称是否代表相同的对象:

>>> a = b = c = [0, 3, 5]
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488

列表是可变的;这意味着您可以在不创建新对象的情况下就地更改值。但是,这取决于您如何更改值:

>>> a[0] = 1
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488
>>> print(a, b, c)
[1, 3, 5] [1, 3, 5] [1, 3, 5]

如果您将新列表分配给a,则其ID将更改,因此不会影响bc的值:

>>> a = [1, 8, 5]
>>> print(id(a), id(b), id(c))
139423880 46268488 46268488
>>> print(a, b, c)
[1, 8, 5] [1, 3, 5] [1, 3, 5]

整数是不可变的,因此您不能在不创建新对象的情况下更改值:

>>> x = y = z = 1
>>> print(id(x), id(y), id(z))
507081216 507081216 507081216
>>> x = 2
>>> print(id(x), id(y), id(z))
507081248 507081216 507081216
>>> print(x, y, z)
2 1 1

1
id不一定是内存位置。就像文档所说的那样,它返回“ identity…一个整数……在此对象的生存期内,保证该对象是唯一且恒定的”。CPython恰好将内存地址用作id,但是其他Python实现可能不使用。例如,PyPy不会。并且说“两个变量指向相同的内存位置”对任何理解C风格的人都产生了误导。“同一个对象的两个名称”既更准确,又减少了误导。
13年

@abarnert感谢您的澄清,我更新了答案。
jurgenreza

7

在第一个示例中,a = b = c = [1, 2, 3]您实际上是在说:

 'a' is the same as 'b', is the same as 'c' and they are all [1, 2, 3]

如果要将“ a”设置为1,将“ b”设置为“ 2”,将“ c”设置为3,请尝试以下操作:

a, b, c = [1, 2, 3]

print(a)
--> 1
print(b)
--> 2
print(c)
--> 3

希望这可以帮助!


4

简而言之,在第一种情况下,您要为分配多个名称list。在内存中仅创建一个列表副本,并且所有名称均指向该位置。因此,使用任何名称更改列表实际上都会修改内存中的列表。

在第二种情况下,将在内存中创建相同值的多个副本。因此,每个副本彼此独立。


3

您需要的是:

a, b, c = [0,3,5] # Unpack the list, now a, b, and c are ints
a = 1             # `a` did equal 0, not [0,3,5]
print(a)
print(b)
print(c)

3

执行我需要的代码可能是这样的:

# test

aux=[[0 for n in range(3)] for i in range(4)]
print('aux:',aux)

# initialization

a,b,c,d=[[0 for n in range(3)] for i in range(4)]

# changing values

a[0]=1
d[2]=5
print('a:',a)
print('b:',b)
print('c:',c)
print('d:',d)

结果:

('aux:', [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]])
('a:', [1, 0, 0])
('b:', [0, 0, 0])
('c:', [0, 0, 0])
('d:', [0, 0, 5])
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.