python 对象引用,可变性,垃圾回收
python变量类似于引用式变量,因此可以理解为附加在对象上的标注
charles = {"name": 'charles', "age": 30} lews = charles print(charles==lews) print(charles is lews)
元组的相对不可变性:
集合,列表,字典等python集合保存的式=是对象的引用,元组的不可变性指的是tuple数据结构的物理内容(及保存的引用)不可变,与引用的对象无关。
t1 = (1, 2, [3, 4]) t2 = (1, 2, [3, 4]) print(t1 == t2) # true print(t1 is t2) # false print(id(t1[-1])) t1[-1].append(5) print(t1) print(id(t1[-1])) # 依然不可变 这才是元组不可变的本质
副本与源对象, 浅复制
复制列表等可变对象,有两种方法:
# method 1 a = [12, 23, [11, 22], (7, 8, 9)] b = list(a) # a是源对象, b是副本 # method 2 b = a[:] # 复制
copy模块提供的copy()和deepcopy()能为任意对象做求浅复制和深复制
from copy import copy, deepcopy class Bus: def __init__(self, passengers=None): if passengers is None: self.passengers = [] else: self.passengers = list(passengers) def drop(self, name): self.passengers.remove(name) def pick(self, name): self.passengers.append(name) def __repr__(self): return "Passengers are: " + str(self.passengers) if __name__=="__main__": passenger = ["Lily", "Nosy", "Tom"] bus1 = Bus(passenger) bus2 = copy(bus1) # copy函数 bus3 = deepcopy(bus1) # deepcopy函数 print(bus1) bus1.pick("liu") print(bus1) print(bus2) print(bus3)
输出结果:
此外,可以通过实现特殊方法__copy__()和__deepcopy__()能够控制copy的行为。
参数传递
python唯一支持的参数传递模式是共享传参。共享传参指函数的各个形式参数获得实参中各个引用的副本,也就是说函数内部的实参是形参的别名。
可对上面的程序做一个小的修改
from copy import copy, deepcopy class Bus: def __init__(self, passengers=None): if passengers is None: self.passengers = [] else: # self.passengers = list(passengers) # 相当于创建实参的一个副本,所以形参变化,实参不变化 self.passengers = passengers # 形参是实参的别名,python中参数传递的方式 def drop(self, name): self.passengers.remove(name) def pick(self, name): self.passengers.append(name) def __repr__(self): return "Passengers are: " + str(self.passengers) if __name__=="__main__": passenger = ["Lily", "Nosy", "Tom"] bus1 = Bus(passenger) bus2 = copy(bus1) # copy函数 bus3 = deepcopy(bus1) # deepcopy函数 print(bus1) bus1.pick("liu") print(bus1) print(bus2) print(bus3) print(passenger) # pick方法对函数内部的形参做了修改 # 形参是实参的引用,所以说实参也会跟着变化
不同类型的对象的实参,传入函数中后,会受到不同的影响
1. 数组和元组没变
2. 列表变了
不适用可变类型作为函数的默认值
可选参数可以有默认值,这是python的一个特性,能保证API在进化的时候向后兼容, 但是,应该避免使用可变的对象作为参数的默认值。
例如对上面的代码作如下修改:
passenger = ["Lily", "Nosy", "Tom"] bus1 = Bus(passenger) bus1.pick("wang") bus1.drop("Lily") print(bus1) bus2 = Bus() # 使用了默认的参数值 print(bus2) bus2.pick("zhang") bus3 = Bus() # 使用默认参数,但此时默认的参数(可变对象)已经被修改 print(bus3) # 修改了之后。bus3中有值了
输出结果:
在修改bus2的时候,实际上修改了传递给构造方法的那个列表,所以bus3中出现了一个“幽灵” 乘客。
正确的做法是:Bus类应该自己维护passenger列表,应该做到在构造方法中将实参的副本传递给实参。这样就不会影响初始化的时候传递的参数了。
del和垃圾回收:
del语句删除名称,而不是对象,del命令可能会导致对象被当作垃圾回收,但是仅当删除的变量保存的是对象的最后一个引用的时候。
cpython中,垃圾回收采用的算法是引用计数,实际上,每个对象都会统计有多少个引用指向自己,当引用计数归零时,对象立即会被销毁。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)