如果两个变量指向同一个对象,为什么重新分配一个变量不会影响另一个变量?

2025-03-26 09:10:00
admin
原创
26
摘要:问题描述:我试图理解变量在 Python 中的工作方式。假设我在变量中存储了一个对象a:>>> a = [1, 2, 3] 如果我分配a给b,两者都指向同一个对象:>>> b = a >>> b is a True 但如果我重新分配a或b,情况就不再如此:...

问题描述:

我试图理解变量在 Python 中的工作方式。假设我在变量中存储了一个对象a

>>> a = [1, 2, 3]

如果我分配ab,两者都指向同一个对象:

>>> b = a
>>> b is a
True

但如果我重新分配ab,情况就不再如此:

>>> a = {'x': 'y'}
>>> a is b
False

这两个变量现在具有不同的值:

>>> a
{'x': 'y'}
>>> b
[1, 2, 3]

我不明白为什么变量现在不同了。为什么a is b不再正确?有人能解释一下发生了什么吗?


解决方案 1:

Python 中的名称指的是对象。对象独立于名称而存在,名称也独立于其所引用的对象而存在。

# name a
a = 1337
    # object 1337

当将“名称赋给名称”时,右侧将计算为所引用的对象。与2 + 2计算为类似4a计算为原始1337

# name b
b = a
    # object referred to by a -> 1337

此时,我们有a -> 1337b -> 1337- 请注意,两个名字都不认识对方!如果我们测试a is b,两个名称将被评估为同一个对象,这显然是相等的。

重新分配名称只会改变该名称所指的内容 - 不存在可以更改其他名称的联系。

# name a - reassign
a = 9001
  # object 9001

此时,我们有a -> 9001b -> 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 -+ -\n               \n                --> +- <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`nmn`

+- name m -+ -\n                                 
                --> +- […] -+     +--- <@0> -+
               /    |    @0 |  -> |     1337 |
+- name n -+ -/     +-------+     +----------+

# m += [9001]

+- name m -+ -\n                                 
                --> +- […] -+     +--- <@0> -++--- <@1> -+
               /    | @0 @1 |  -> |     1337 ||     9001 |
+- name n -+ -/     +-------+     +----------++----------+

解决方案 2:

在 Python 中,所有变量都存储在字典或看起来很像字典的结构中(例如,locals()可以将当前范围/命名空间显示为字典)。

注意PyObject*是 CPython 概念。我不确定在其他 Python 实现中情况如何。

因此,将 Python 变量视为具有精确内存位置的 C 变量是错误的。它们的PyObject*(指针或内存位置),而不是实际的原始值。由于变量本身只是指向PyObject*指针的字典中的条目,因此更改变量的值实际上是为其提供了一个不同的内存地址。

在 CPython 中,这些值被和PyObject*使用(与 相同)。id`isa is bid(a) == id(b)`

例如,让我们考虑一下简单的代码行:

# x: int
x += 1

实际上改变了与变量关联的内存位置。这是因为它遵循以下逻辑:

LOAD_FAST (x)
LOAD_CONST (1)
INPLACE_ADD
STORE_FAST (x)

字节码大致如下:

  1. 查找 x 的值。在 CPython 中,它是PyObject*指向PyLongLong或 这样的(int来自 Python 用户空间)

  2. 从常量内存地址加载值

  3. 将两个值相加。这将产生一个新的PyObject*,它也是一个int

  4. 将与之关联的值设置x为这个新指针

TL;DR:Python 中的所有内容(包括基元)都是对象。变量本身并不存储值,而是存储封装它们的指针。重新分配变量会更改与该名称关联的指针,而不会更新保存在该位置的内存。

解决方案 3:

假设我在变量 a 中存储了一个对象”——这就是你错的地方。

Python 对象不存储在变量,而是通过变量引用

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

ab引用同一个对象。该list对象的引用计数为 2,因为有两个名称引用它。

a = {'x': 'y'}

a不再引用同一个list对象,而是引用一个dict对象。这会减少该对象的引用计数list,但b仍引用该对象,因此该对象的引用计数现在为 1。

b = None

这意味着b现在引用了None对象(该对象的引用计数非常高,许多名称都引用了None)。该list对象的引用计数再次减少,并降至零。此时,该list对象可以被垃圾回收,内存被释放(何时发生这种情况无法保证)。

参见sys.getrefcount

解决方案 4:

我用外行人能理解的语言向你解释,以便你能够轻松理解。

案例1

a = [1, 2, 3]
b = a
print(b is a)

的值a[1,2,3]。现在我们将也[1,2,3]赋给。因此两者具有相同的值,因此 = 。b`ab is aTrue`

下一步,

a = {'x': 'y'}
print(a is b) 

现在您将 的值更改a为 ,{'x':'y'} 我们的b值仍然与 相同[1,2,3]。因此现在a is bFalse

案例 2如果您已完成以下操作:-

a = [1, 2, 3]
b = a
print(b is a)
a = {'x': 'y'}
b = a  # Reassigning the value of b.
print(a is b)

重新分配 的值后a,我还将重新分配 的值。因此,在两种情况下b您都会得到。True

我希望这对你有帮助。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用