Python 有 names 引用 objects。对象与名称分开存在,名称与其所指的对象分开存在。
# 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 - 请注意,两个名字都不认识另一个!如果我们测试a is b,两个名称都评估到同一个对象,显然是相等的。
重新分配名称只会更改该名称所指的内容 - 没有任何联系可以更改其他名称。
# name a - reassign
a = 9001
# object 9001
此时,我们有a -> 9001 和b -> 1337。如果我们现在测试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。
但是,对名称的某些操作会更改名称和所有别名看到的值。
# 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 -+ -/ +-------+ +----------++----------+