Python 不可变(Immutable)对象与变量传递机制详解
· 3 min read
一、Python 不可变(Immutable)对象
1. 不可变对象类型
Python 中的不可变对象包括:
- 数字类型:
int
,float
,bool
,complex
- 字符串:
str
- 元组:
tuple
- 冻结集合:
frozenset
bytes
2. 不可变特性表现
a = 10
b = a
a = 20 # 创建新对象,而不是修改原有对象
print(b) # 输出10,b不受a改变影响
s = "hello"
s[0] = "H" # 报错:TypeError,字符串不可变
3. 不可变对象的内存机制
a = 256
b = 256
print(a is b) # True,小整数池优化
c = 257
d = 257
print(c is d) # False,大整数不共享
二、可变(Mutable)对象
1. 可变对象类型
- 列表:
list
- 字典:
dict
- 集合:
set
- 字节数组:
bytearray
- 自定义类对象
2. 可变特性表现
lst1 = [1, 2, 3]
lst2 = lst1
lst1.append(4)
print(lst2) # [1, 2, 3, 4],lst2也被修改
三、Python 变量传递机制
1. 按对象引用传递
Python 中所有参数传递都是按对象引用传递,但效果取决于对象类型:
- 不可变对象:函数内修改会创建新对象,不影响原始值
- 可变对象:函数内修改会影响原始对象
2. 函数参数传递示例
(1) 传递不可变对象
def modify_num(x):
x = x + 10
print("函数内x:", x) # 15
num = 5
modify_num(num)
print("原始num:", num) # 5(未改变)
(2) 传递可变对象
def modify_list(lst):
lst.append(4)
print("函数内lst:", lst) # [1, 2, 3, 4]
my_list = [1, 2, 3]
modify_list(my_list)
print("原始my_list:", my_list) # [1, 2, 3, 4](已改变)
3. 避免意外修改的解决方案
(1) 使用不可变对象
def safe_func(t):
t = t + (4,) # 创建新元组
print(t) # (1, 2, 3, 4)
orig_tuple = (1, 2, 3)
safe_func(orig_tuple)
print(orig_tuple) # (1, 2, 3)
(2) 创建副本
def safe_modify(lst):
lst_copy = lst.copy() # 或 list(lst) 或 lst[:]
lst_copy.append(4)
print("副本:", lst_copy) # [1, 2, 3, 4]
original = [1, 2, 3]
safe_modify(original)
print("原始:", original) # [1, 2, 3]
四、高级主题:深浅拷贝
特性 | 浅拷贝 (Shallow Copy) | 深拷贝 (Deep Copy) |
---|---|---|
复制深度 | 只复制最外层容器,内部元素保持引用关系 | 递归复制所有嵌套对象,创建完全独立的新对象 |
内存占用 | 较少 | 较多 |
执行速度 | 较快 | 较慢 |
适用场景 | 简单对象或明确不需要嵌套复制的情况 | 复杂嵌套对象需要完全独立的情况 |
修改影响 | 修改嵌套对象会影响原对象 | 修改任何级别都不会影响原对象 |
1. 浅拷贝(shallow copy)
import copy
lst1 = [1, [2, 3], 4]
lst2 = copy.copy(lst1)
lst1[1][0] = 99
print(lst2) # [1, [99, 3], 4] 嵌套对象被修改
2. 深拷贝(deep copy)
import copy
lst1 = [1, [2, 3], 4]
lst2 = copy.deepcopy(lst1)
lst1[1][0] = 99
print(lst2) # [1, [2, 3], 4] 完全独立
五、实际应用建议
- API 设计:如果函数不应修改传入的可变参数,应在文档中明确说明
- 线程安全:不可变对象天然线程安全
- 字典键:只能使用不可变对象作为字典键
- 性能考虑:频繁修改字符串应考虑使用
list
或bytearray