APP下载

浅谈Python 中的可变与不可变数据类型

2020-12-28陈玲

数字技术与应用 2020年11期
关键词:数据类型调用变量

陈玲

(泸州职业技术学院,四川泸州 646000)

0 引言

初学Python的时候,可能会对Python中的可变类型和不可变类型感到疑惑,而我们实际工作中也常常会遇到Python中的可变类型和不可变类型,正确的使用它们才能产生我们预期的效果。下面本文将从概念、判断、常见的可变与不可变数据类型、函数参数传递的区别等方面对Python的可变类型和不可变类型进行分析。

1 可变与不可变类型的概念

从字面意思来理解,可变类型对象就是指对象的内容可变,而不可变类型对象就是指对象的内容不可变。我们来看一个例子:

示例源代码 示例运行结果a = 10 print(id(a))a = 20 print(id(a))b = [10]print(id(b))b[0] = 20 print(id(b))140727772846016 140727772846336 2778581568704 2778581568704

在上面的例子中,id函数用于获取对象的内存地址。把a的值从10修改成20时,a对象的内存地址发生了变化,说明执行a=20时并不是将a所指向的内存地址中存储的10直接修改成20,而是在内存中新申请了一段内存空间来存储对象20,然后a再指向对象20,所以两次id(a)的值的不同,如图1所示。而b[0]=20,修改列表的第一个元素,两次id(b)的值相同,说明b指向的内存空间不变,是在存储10的原有内存空间基础上直接将10修改成20,如图2所示。

图1 修改不可变类型

图2 修改可变类型

当修改该数据类型对应变量的值,如果存储变量值对应的内存地址发生了改变,这种数据类型就是不可变数据类型。如果存储变量值对应的内存地址没有发生改变,这种数据类型就是可变数据类型。如上例中的整型,将a从10修改成20,存储20的内存地址已经是一个新地址了,a的引用地址从140727772846016变成140727772846336,已经发生了变化,所以整型是不可变数据类型。将列表b的第一个元素修改成20,b的引用地址依旧是1309544287232,没有发生改变,所以列表是可变数据类型。

从结果来看,不可变类型变量和可变类型变量的值都能修改,所谓不可变类型,是指不能直接修改变量所引用的内存地址中的这个值,当要改变变量的赋值时,其实是在内存中重新开辟了一段内存空间,将修改后的数据存储在这个新的内存地址里,变量不再引用原数据的内存地址而是引用新的内存地址了。所谓可变类型,是指变量所引用的内存地址处的值是可以改变的。

2 Python中常见的可变类型和不可变类型

如何判断Python中的数据类型是可变类型还是不可变类型呢?根据上述的解释,只需要判断变量所引用的内存地址处的值能否直接修改,即修改后判断变量所引用的内存地址值是否改变,通过id()来获取变量引用的内存地址,value值改变,id值不变即为可变类型,value值改变,id值也随之改变即为不可变类型。即只需要在改变value值时,使用id()查看变量所引用的内存地址是否变化,就可以知道这种数据类型是可变的还是不可变的。例如:

示例源代码 示例运行结果flag = T rue print(id(flag))flag = False print(id(flag))140727770666832 140727770666864

从结果来看,当改变布尔类型变量的值时,变量id值也改变了,所以布尔类型是不可变类型。举一反三,我们能得出判断出Python中常用的可变类型和不可变类型,如下:

2.1 Python中常用的不可变类型

整数、浮点数、布尔类型、字符串、元组。

2.2 Python中常用的可变类型

列表、字典、集合。

3 可变类型和不可变类型在Python函数参数传递中的应用与区别

在调用Python函数时,根据函数的定义情况,有时需要传递实参。当函数修改了形参的值时,函数调用结束时,实参类型的不同会导致实参最终的结果不同。我们来看一个传递不可变类型实参的例子:

传递不可变类型a = 10 print("调用前,a = {},引用地址为{}。".form at(a,id(a)))def m odifyInt(b):# 定义函数print("b 的引用地址为{}。".form at(id(b)))b = 20 print("修改后b 的引用地址为{}。".form at(id(b)))m odifyInt(a)# 调用函数print("调用后,a = {},引用地址为{}。".form at(a,id(a)))运行结果如下:调用前,a = 10,引用地址为140727770945472。b 的引用地址为140727770945472。修改后b 的引用地址为140727770945792。调用后,a = 10,引用地址为140727770945472。

图3 传递不可变类型

函数modifyInt()修改形参b的值为20,调用函数时传递了实参a,是整数,为不可变类型,函数调用结束后,a的值仍为10,函数虽然修改了形参的值,但并没有修改实参的值。从运行结果来看,形参b的引用地址和实参a的引用地址相同,说明函数调用时实际上是把a的引用传递给了b,因为b为不可变类型,所以b=20时,是在内存重新开辟了一段内存空间存储20,然后b的引用指向了这个新的内存地址。如图3所示,当传递不可变类型的实参时,传递的是引用,函数修改形参的值并不会修改实参的值[1]。

接下来看一个传递可变类型实参的例子:

传递可变类型a = [10]print("调用前,a = {},引用地址为{}。".form at(a,id(a)))def m odifyList(b):# 定义函数print("b 的引用地址为{}。".form at(id(b)))b[0] = 20 print("修改后b 的引用地址为{}。".form at(id(b)))m odifyList(a)# 调用函数print("调用后,a = {}".form at(a))运行结果如下:调用前,a = [10],引用地址为1782725414080。b 的引用地址为1782725414080。修改后b 的引用地址为1782725414080。调用后,a = [20]

函数modifyList()修改形参b的第一个元素为20,调用函数时传递了实参a,是列表,为可变类型,函数调用结束后,a的第一个元素被修改成了20。函数修改了形参的值,导致了实参值的改变。从运行结果来看,形参b的引用地址和实参a的引用地址相同,说明函数调用时仍然是把a的引用传递给了b,因为b为可变类型,所以在b引用指向的内存地址中直接修改了这个值,而没有重新开辟内存空间来保存这个修改后的值。如图4所示。当传递可变类型的实参时,传递的是引用,函数修改形参的值后,实参的值也发生改变[2]。

图4 传递可变类型

4 结语

对于不可变类型,变量(引用)指向的地址的内容是不可变的,改变变量的值只是将变量(引用)指向了新的地址。对于可变类型,变量(引用)指向的地址的内容是可变的,改变变量的值就是直接对原地址内容的改变。整数、浮点数、字符串、布尔类型、元组都是不可变类型,列表、字典、集合是可变类型,在进行函数参数传递时,虽然都是引用传递,但要区分传递的是可变类型还是不可变类型。传递不可变类型时,改变形参的值,并不会改变实参的值;传递可变类型时,改变形参的值,会改变实参的值。

猜你喜欢

数据类型调用变量
详谈Java中的基本数据类型与引用数据类型
抓住不变量解题
也谈分离变量
如何理解数据结构中的抽象数据类型
核电项目物项调用管理的应用研究
LabWindows/CVI下基于ActiveX技术的Excel调用
基于系统调用的恶意软件检测技术研究
SL(3,3n)和SU(3,3n)的第一Cartan不变量
分离变量法:常见的通性通法
范畴数据类型上的子类型*