Python 数据类型之可变与不可变类型详解(十)

Python 数据类型之可变与不可变类型详解

一、引言

在 Python 编程中,数据类型是构建程序的基础。而理解数据类型的可变与不可变特性,对于正确编写和优化代码至关重要。可变类型与不可变类型的区别影响着数据的存储、传递和操作方式。本文将深入剖析 Python 中数据类型的可变与不可变特性,通过丰富的源码示例和详细注释,帮助读者掌握这一核心概念。

二、不可变数据类型

2.1 数字类型(int、float、complex)

数字类型(整数、浮点数、复数)是典型的不可变类型。一旦创建,其值就不能被修改。若尝试修改,实际上是创建了一个新的对象。

python 复制代码
# 定义一个整数变量
num = 10
print(f"num 的值: {num},内存地址: {id(num)}")  # 输出 num 的值和内存地址

# 对 num 进行重新赋值
num = num + 5
print(f"重新赋值后 num 的值: {num},内存地址: {id(num)}")  # 输出新的 num 值和内存地址

# 可以看到重新赋值后,内存地址发生了变化,说明创建了新的对象

在上述代码中,num 最初被赋值为 10,此时有对应的内存地址。当执行 num = num + 5 时,Python 并没有直接修改原 num 对象的值,而是创建了一个新的整数对象 15,并将 num 指向这个新对象,内存地址也随之改变。

浮点数和复数类型同样具有不可变特性:

python 复制代码
# 定义一个浮点数变量
float_num = 3.14
print(f"float_num 的值: {float_num},内存地址: {id(float_num)}")

# 重新赋值
float_num = 3.1415
print(f"重新赋值后 float_num 的值: {float_num},内存地址: {id(float_num)}")
# 内存地址改变,创建了新的浮点数对象

2.2 字符串类型(str)

字符串也是不可变类型。对字符串进行的任何修改操作,都会生成一个新的字符串对象。

python 复制代码
# 定义一个字符串
string = "Hello"
print(f"string 的值: {string},内存地址: {id(string)}")

# 尝试修改字符串(拼接操作)
new_string = string + ", World"
print(f"new_string 的值: {new_string},内存地址: {id(new_string)}")
# 可以看到新字符串的内存地址与原字符串不同,创建了新对象

new_string = string + ", World" 中,Python 不会在原 string 对象上直接修改,而是创建了一个新的字符串 "Hello, World" 并赋值给 new_string

2.3 元组类型(tuple)

元组是不可变的序列类型,一旦创建,其元素不能被修改、删除或添加。

python 复制代码
# 定义一个元组
my_tuple = (1, 2, 3)
print(f"my_tuple 的值: {my_tuple},内存地址: {id(my_tuple)}")

# 尝试修改元组中的元素(会报错)
# my_tuple[0] = 4  # 这行代码会引发 TypeError 异常

# 虽然元组本身不可变,但如果元组元素是可变对象,可变对象内部可以修改
nested_tuple = ([1, 2], 3)
nested_tuple[0].append(4)  # 这里修改的是元组中列表元素的内容,而不是元组本身
print(f"修改后的 nested_tuple: {nested_tuple}")

直接修改元组元素会报错,但元组中包含可变对象时,可变对象的内容可以被修改 ,不过这并不改变元组本身不可变的特性。

三、可变数据类型

3.1 列表类型(list)

列表是典型的可变数据类型,可以动态地添加、删除和修改元素。

python 复制代码
# 定义一个列表
my_list = [1, 2, 3]
print(f"my_list 的值: {my_list},内存地址: {id(my_list)}")

# 修改列表中的元素
my_list[0] = 10
print(f"修改后的 my_list: {my_list},内存地址: {id(my_list)}")
# 可以看到修改元素后,列表的内存地址不变,在原对象上进行了修改

# 添加元素
my_list.append(4)
print(f"添加元素后的 my_list: {my_list},内存地址: {id(my_list)}")
# 同样,添加元素后内存地址不变

# 删除元素
my_list.remove(2)
print(f"删除元素后的 my_list: {my_list},内存地址: {id(my_list)}")
# 删除元素后内存地址依然不变

在对列表进行各种操作时,始终在同一个内存地址的列表对象上进行修改,不会创建新的列表对象。

3.2 字典类型(dict)

字典是可变的数据类型,可以随时添加、修改和删除键值对。

python 复制代码
# 定义一个字典
my_dict = {"name": "Alice", "age": 25}
print(f"my_dict 的值: {my_dict},内存地址: {id(my_dict)}")

# 修改字典中的值
my_dict["age"] = 26
print(f"修改后的 my_dict: {my_dict},内存地址: {id(my_dict)}")
# 内存地址不变,在原字典对象上修改

# 添加新的键值对
my_dict["city"] = "New York"
print(f"添加键值对后的 my_dict: {my_dict},内存地址: {id(my_dict)}")
# 内存地址不变

# 删除键值对
del my_dict["name"]
print(f"删除键值对后的 my_dict: {my_dict},内存地址: {id(my_dict)}")
# 内存地址仍然不变

字典的各种操作都是在原字典对象上进行,不会创建新的字典对象 。

3.3 集合类型(set)

集合是可变的无序数据类型,可以添加、删除元素。

python 复制代码
# 定义一个集合
my_set = {1, 2, 3}
print(f"my_set 的值: {my_set},内存地址: {id(my_set)}")

# 添加元素
my_set.add(4)
print(f"添加元素后的 my_set: {my_set},内存地址: {id(my_set)}")
# 内存地址不变

# 删除元素
my_set.remove(2)
print(f"删除元素后的 my_set: {my_set},内存地址: {id(my_set)}")
# 内存地址不变

集合的操作同样是在原集合对象上进行,不会创建新的集合对象。

四、可变与不可变类型在函数传递中的差异

4.1 不可变类型作为函数参数

当不可变类型作为函数参数传递时,函数内部对参数的修改不会影响外部变量。

python 复制代码
def modify_number(num):
    num = num + 1  # 在函数内部修改 num 的值,实际创建了新对象
    print(f"函数内部 num 的值: {num},内存地址: {id(num)}")

# 定义一个整数变量
outer_num = 5
print(f"调用函数前 outer_num 的值: {outer_num},内存地址: {id(outer_num)}")
modify_number(outer_num)
print(f"调用函数后 outer_num 的值: {outer_num},内存地址: {id(outer_num)}")
# 可以看到函数内外的内存地址不同,外部变量未被修改

modify_number 函数中,num = num + 1 创建了新的整数对象,函数内部的 num 指向新对象,而外部的 outer_num 不受影响。

4.2 可变类型作为函数参数

当可变类型作为函数参数传递时,函数内部对参数的修改会影响外部变量。

python 复制代码
def modify_list(lst):
    lst.append(4)  # 在函数内部修改列表,直接在原对象上操作
    print(f"函数内部 lst 的值: {lst},内存地址: {id(lst)}")

# 定义一个列表
outer_list = [1, 2, 3]
print(f"调用函数前 outer_list 的值: {outer_list},内存地址: {id(outer_list)}")
modify_list(outer_list)
print(f"调用函数后 outer_list 的值: {outer_list},内存地址: {id(outer_list)}")
# 函数内外内存地址相同,外部列表被修改

modify_list 函数中,lst.append(4) 直接在传入的列表对象上进行修改,所以外部的 outer_list 也会发生变化。

五、总结与展望

5.1 总结

Python 中的数据类型根据其可变与不可变特性分为两类:

  • 不可变类型:包括数字、字符串和元组。这些类型的对象一旦创建,其值不能被修改,任何看似修改的操作实际上都是创建新对象。不可变类型在多线程环境下更安全,也适合用作字典的键。
  • 可变类型:包括列表、字典和集合。可变类型可以动态地添加、删除和修改元素,在数据频繁变动的场景下使用更加灵活 。

理解可变与不可变类型的特性,对于正确处理数据传递、避免意外的数据修改以及优化内存使用都具有重要意义。

5.2 展望

在未来的 Python 编程中,随着项目复杂度的增加,对数据类型特性的理解将愈发关键。在大数据处理、高并发编程等场景下,合理选择可变与不可变类型能够显著提升程序性能和稳定性。同时,随着 Python 语言的发展,可能会出现更多基于这些特性的优化和新的编程范式,开发者需要持续深入学习,以更好地驾驭 Python 编程。

相关推荐
Narutolxy1 小时前
大模型数据分析破局之路20250512
人工智能·chatgpt·数据分析
浊酒南街1 小时前
TensorFlow中数据集的创建
人工智能·tensorflow
码上淘金2 小时前
【Python】Python常用控制结构详解:条件判断、遍历与循环控制
开发语言·python
Brilliant Nemo2 小时前
四、SpringMVC实战:构建高效表述层框架
开发语言·python
2301_787552873 小时前
console-chat-gpt开源程序是用于 AI Chat API 的 Python CLI
人工智能·python·gpt·开源·自动化
懵逼的小黑子3 小时前
Django 项目的 models 目录中,__init__.py 文件的作用
后端·python·django
layneyao3 小时前
AI与自然语言处理(NLP):从BERT到GPT的演进
人工智能·自然语言处理·bert
Y3174293 小时前
Python Day23 学习
python·学习
Ai尚研修-贾莲4 小时前
Python语言在地球科学交叉领域中的应用——从数据可视化到常见数据分析方法的使用【实例操作】
python·信息可视化·数据分析·地球科学
jndingxin4 小时前
OpenCV 的 CUDA 模块中用于将多个单通道的 GpuMat 图像合并成一个多通道的图像 函数cv::cuda::merge
人工智能·opencv·计算机视觉