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

我尝试使用如下所示的多重赋值来初始化变量,但是我被这种行为弄糊涂了,我希望分别重新分配值列表,我的意思是 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)

302472 次浏览

你需要的是这个:

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)

在你的第一个例子 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

希望这个能帮上忙!

是的,这是预期的行为。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)。

在 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 也改变了。

可以使用 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

如果你是从 C/Java/etc 家族的语言学习 Python 的,它可能会帮助你停止把 a看作一个“变量”,而开始把它看作一个“名字”。

abc不是具有相同值的不同变量; 它们是具有相同值的不同名称。变量有类型、身份、地址等等。

名字里没有这些。当然,价值观可以这样做,并且您可以为相同的值使用许多名称。

如果你给 Notorious B.I.G.一个热狗,* Biggie SmallsChris Wallace有一个热狗。如果将 a的第一个元素更改为1,则 bc的第一个元素为1。

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

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

然后你会问:

和这个有什么不同?

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

在这里,将名称 e重新绑定到值 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正在调用 list 对象上的一个方法。(相当于 a.__setitem__(0, 1)。)所以,它根本不是 真的重新绑定任何东西。就像打 my_object.set_something(1)。当然,为了实现这个方法,对象可能正在重新绑定一个实例属性,但这并不重要; 重要的是你没有赋值任何东西,你只是变异了对象。a[0]=1也是如此。


User570826问道:

如果我们有呢,a = b = c = 10

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

但是在这种情况下,值是 int,并且 int是不可变的。无论哪种情况,您都可以将 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]。没有办法把 int3的值改成其他值。int3永远是10,就像吸血鬼克劳迪娅永远是5(至少在她被克里斯汀邓斯特取代之前)。


* 警告: 不要给 Notorious B.I.G. 热狗,黑帮说唱僵尸午夜后绝不能吃东西。

我需要的代码可以是这样的:

# 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])

咳咳

>>> 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'}
>>>

简单地说,在第一种情况下,您为 list分配了多个名称。在内存中只创建列表的一个副本,并且所有名称都引用该位置。因此,使用任何名称更改列表实际上都会修改内存中的列表。

在第二种情况下,在内存中创建相同值的多个副本。所以每个副本都是相互独立的。

行为是正确的。但是,所有的变量将共享相同的引用。请注意下面的行为:

>>> a = b = c = [0,1,2]
>>> a
[0, 1, 2]
>>> b
[0, 1, 2]
>>> c
[0, 1, 2]
>>> a[0]=1000
>>> a
[1000, 1, 2]
>>> b
[1000, 1, 2]
>>> c
[1000, 1, 2]

所以,是的,这是不同的,在这个意义上,如果你分配 a,b 和 c 不同在一个单独的行,改变一个不会改变其他。

为了赋予多个变量相同的值,我更喜欢 list

a, b, c = [10]*3#multiplying 3 because we have 3 variables
print(a, type(a), b, type(b), c, type(c))

产出:

10 <class 'int'> 10 <class 'int'> 10 <class 'int'>

初始化多个对象:

import datetime


time1, time2, time3 = [datetime.datetime.now()]*3


print(time1)
print(time2)
print(time3)

产出:

2022-02-25 11:52:59.064487
2022-02-25 11:52:59.064487
2022-02-25 11:52:59.064487

例如: 基本上 a = b = 10意味着 ab都指向内存中的 10,你可以用 id(a)id(b)来测试,它们的结果与 a is bTrue完全相等。

is匹配内存位置,但不匹配其值,而 ==匹配该值。

让我们假设,您希望将 a的值从 10更新到 5,因为内存位置指向您将体验到的相同内存位置,由于初始声明,b的值也将指向 5

结论是,只有在您知道结果的情况下才使用这种方法,否则只需使用 ,分隔赋值,如 a, b = 10, 10,并且不会因为不同的内存位置而在更新任何值时面临上述解释的结果。