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中,垃圾回收采用的算法是引用计数,实际上,每个对象都会统计有多少个引用指向自己,当引用计数归零时,对象立即会被销毁。

posted @   Alpha205  阅读(74)  评论(0编辑  收藏  举报
编辑推荐:
· 从 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)
点击右上角即可分享
微信分享提示