Python变量机制解析:引用语义与对象绑定
本文深入解析Python中变量的本质,指出Python采用引用语义而非值语义。变量并非存储数据的容器,而是指向对象的标签。文章详细区分了可变与不可变对象在内存中的行为差异,结合id()函数与代码示例,阐明变量赋值、重新绑定及对象修改时的内存地址变化规律。
一、Python变量
在多数编程语言中,将值赋予名称的操作通常被称为“变量赋值”或“将值存入变量”。然而,Python的设计机制与其他许多语言存在显著差异:它并非将值直接存储在变量所代表的内存空间中,而是将名称“绑定”到对象上(即名字指向对象)。因此,部分Python开发者认为Python中不存在传统意义上的变量,仅存在通过名称引用的对象。
Python中的变量机制与其他开发语言(如C语言)存在以下主要区别:
在C语言中,变量可被视为一个“容器”,赋值操作即是将值装入该容器:
定义一个整型变量 int a = 1;

对变量a进行重新赋值 a = 2;

将变量a的值赋给另一个变量b,即 int b = a;

此过程会创建一个新的变量b(容器),并将a容器内的值复制一份存入b中。
而在Python中,变量更像是一个“标签”,贴在具体的值(对象)上,通过名称即可找到对应的值。
定义一个变量 a = 1

对变量a重新赋值 a = 2

将变量a的值赋给另一个变量b,即 b = a

系统会创建一个新的标签b,并将其与a同时贴在同一个值对象上。
为了更深入地理解Python中变量的这一特性,下文将展开详细介绍。
1.1 第一点
首先说明第一点:变量的实现方式主要分为引用语义和值语义。
Python语言采用引用语义实现变量,即变量中保存的是对象在内存中的引用地址(而非对象本身)。这种方式使得变量所需的存储空间大小保持一致,因为只需保存一个引用。相比之下,C等语言采用值语义,直接将值存储在变量的存储区中,例如C语言中int类型变量需占用4个字节来存储整数值。
Python中变量与对象的引用关系,类似于C语言中指针变量与所指向地址之间的关系。
在Python的数据结构中,对象分为可变对象和不可变对象。基本数据类型如int(整型)、float(浮点型)、tuple(元组)、str(字符串)属于不可变对象;而list(列表)、dict(字典)、set(集合)属于可变对象。对于可变对象,其存储的元素引用并未改变,改变的是引用所指向的具体值。
采用引用语义存储的仅是变量值所在的内存地址,而非变量值本身。
1.2 第二点
接下来说明第二点:Python中变量、对象与引用三者之间的关系。
在Python中,一切皆对象。每个对象包含三个要素:标识(identity)、类型(type)和值(value)。
☆标识(identity):
用于唯一标识一个对象,通常对应对象在计算机内存中的地址。可以使用内置函数id(obj)来获取对象的唯一标识。
☆类型(type):
类型限制了对象的取值范围及可执行的操作。使用内置函数type(obj)可返回对象所属的类型。
对象中包含标准的头部信息:类型标识符,用于标识对象类型,表明对象存储的数据类型。
每个对象均包含两个标准的头部信息:
1. 类型标识符,用于标识对象的数据类型;
2. 引用计数器,记录当前指向该对象的引用数量。
(回收机制:当变量的引用计数器为0时,对象会自动被清理。注:较小整数型对象具有缓存机制。)
☆值(value):
表示对象所存储的数据信息。使用内置函数print(obj)可直接打印该值。
在Python中,变量用于指向任意对象,本质上是对象的引用。Python变量更像是指针(或称为“贴签”),而非数据存储区域(非“容器”)。
Python中的变量并非装有对象的“容器”,而是贴在对象上的“标签”。给变量赋值,即是将标签贴在对象上;重新赋值,则是将标签从原对象撕下并贴到另一个对象上。
在Python中,变量保存的是对象(值)的引用。采用这种方式,变量的每次初始化都会开辟新的空间,将新内容的地址赋值给变量。id()函数可用于获取变量在内存中的地址。当我们将不同的值赋给变量时,地址会发生变化;若赋予相同的值,地址可能不发生变化(取决于具体实现和缓存)。下面给出示例:

【顺便提示:id()的值并非固定不变——此值表示系统为对象分配的内存地址,在练习时显示的不同值属于正常现象。】

下面是字符串的示例:


在Python中,值存储在内存的某个位置(地址),变量用于引用它们。给变量赋予新值时,原值不会被新值覆盖,变量仅引用了新值。此外,Python的垃圾回收机制会自动清理不再被引用的值,因此无需担心计算机内存中充满被“丢弃”的无效值。
1.3 第三点
接下来说明第三点:可变(mutable)类型对象与不可变(immutable)类型对象。
可变类型对象指在保持其id()不变的情况下,可以改变其取值。
不可变类型对象指具有固定值的对象。不可变对象包括数字(numbers)、字符串(strings)和元组(tuples)。此类对象不能被直接修改。若需存储不同的值,必须创建新对象。不可变对象不允许对自身内容进行修改。若对不可变对象进行赋值,实际上是生成一个新对象,并让变量指向这个新对象。即使对象仅包含数字0或1,逻辑亦然。
由于Python中的变量存放的是对象引用,因此对于不可变对象而言,尽管对象本身不可变,但变量的对象引用是可变的。这种机制有时会导致混淆,看似可变对象发生了变化。如下面的代码:
i = 73
i += 2
不可变对象的特征未变,依然是不可变对象,变化的是创建了新对象并改变了变量的对象引用。参见下图:
对于可变对象,其内容是可以变化的。当对象内容发生变化时,变量的对象引用保持不变。如下面的例子:
m=[5,9]
m+=[6]
参见下图:
二、总结
Python变量指的是名字与对象的绑定(绑定即是将一个对象与一个名字建立联系)。
在绑定时,变量即为名字。
在使用时,变量代表对象的引用。
变量改变的仅有绑定关系。
深入学习:
https://docs.python.org/zh-cn/3.9/reference/datamodel.html#objects-values-and-types
补充说明:
对于复杂的数据类型(列表、集合、字典),若添加某一项元素或几个元素,不会改变其本身的地址,只会改变其内部元素的地址引用;但如果对其重新赋值,则会重新赋予地址以覆盖旧地址,此时地址会发生改变。示例代码如下:
list_ = [1,2,3,4]
print(list_, id(list_))
list_.append(5)
print(list_, id(list_))
#如上代码,因为append前后的list_仍然是同一个对象,只是对象的值发了改变,所以地址不变。
#再如下面的代码
print(list_, id(list_), id(list_[1]))#打印列表、列表的地址、第二个元素的地址
list_[1] = 'aaa' #修改列表
print(list_, id(list_), id(list_[1]))#打印列表、列表的地址、第二个元素的地址
#不难发发现:列表变了、列表的地址没有变、列表内部元素变了、列表内部元素的地址变了
测试运行如下图所示:

到此这篇关于浅谈对Python变量的一些认识理解的文章就介绍到这了,更多相关Python变量内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













