python - 如果两个变量指向同一个对象,为什么重新分配一个变量不会影响另一个?
问题描述
我试图了解变量在 python 中是如何工作的。假设我有一个对象存储在变量中a
:
>>> a = [1, 2, 3]
如果我分配a
给b
,两者都指向同一个对象:
>>> b = a
>>> b is a
True
但如果我重新分配a
or b
,那就不再正确了:
>>> a = {'x': 'y'}
>>> a is b
False
这两个变量现在具有不同的值:
>>> a
{'x': 'y'}
>>> b
[1, 2, 3]
我不明白为什么变量现在不同了。为什么a is b
不再是真的?有人可以解释发生了什么吗?
解决方案
Python 具有引用对象的名称。对象与名称分开存在,名称与其所指的对象分开存在。
# name a
a = 1337
# object 1337
将“名称分配给名称”时,将右侧评估为引用的对象。类似于如何2 + 2
评估为4
,a
评估为原始1337
。
# name b
b = a
# object referred to by a -> 1337
在这一点上,我们有a -> 1337
-b -> 1337
请注意,两个名字都不认识另一个!如果我们 test a is b
,两个名称都被评估为同一个对象,显然是相等的。
重新分配名称只会更改该名称所指的内容 - 其他名称也无法更改。
# name a - reassign
a = 9001
# object 9001
此时,我们有a -> 9001
和b -> 1337
。如果我们现在 test a is b
,这两个名称都会被评估为不同的对象,这些对象并不相同。
如果您来自 C 等语言,那么您习惯于包含值的变量。例如,char a = 12
可以读作“a
是一个包含”的内存区域12
。最重要的是,您可以让多个变量使用相同的内存。为变量分配另一个值会更改共享内存的内容 - 从而更改两个变量的值。
+- char a -+
| 12 |
+--char b -+
# a = -128
+- char a -+
| -128 |
+--char b -+
这不是 Python 的工作方式:名称不包含任何内容,而是引用单独的值。例如,a = 12
可以读作“a
是一个引用值的名称12
”。最重要的是,您可以让多个名称引用相同的值 - 但它仍然是单独的名称,每个名称都有自己的引用。将另一个值分配给一个名称会更改该名称的引用 - 但保持另一个名称的引用不变。
+- name a -+ -\
\
--> +- <12> ---+
/ | 12 |
+- name b -+ -/ +----------+
# a = -128
+- <-128> -+
+- name a -+ -----> | -128 |
+----------+
+- <12> ---+
+- name b -+ -----> | 12 |
+----------+
混淆的一点是可变对象似乎违反了名称和对象的分离。通常,这些是容器(例如list
, dict
, ...),并且默认情况下类表现出相同的行为。
# name m
m = [1337]
# object [1337]
# name n
n = m
# object referred to by m
与普通整数类似,1337
包含整数的列表[1337]
是一个可以由多个独立名称引用的对象。如上所述,n is m
评估为True
并且m = [9001]
不改变n
。
但是,对 name 的某些操作会更改 name和所有 aliases看到的值。
# inplace add to m
m += [9001]
经过这次操作,m == [1337, 9001]
仍然 n is m
成立。事实上,看到的值n
也变成了[1337, 9001]
。这似乎违反了上述行为,其中别名不会相互影响。
这是因为m += [9001]
没有改变什么m
是指。它只会更改(和别名)引用的列表的内容。两者并且仍然引用原始列表对象,其值已更改。m
n
m
n
+- name m -+ -\
\
--> +- […] -+ +--- <@0> -+
/ | @0 | -> | 1337 |
+- name n -+ -/ +-------+ +----------+
# m += [9001]
+- name m -+ -\
\
--> +- […] -+ +--- <@0> -++--- <@1> -+
/ | @0 @1 | -> | 1337 || 9001 |
+- name n -+ -/ +-------+ +----------++----------+