当前位置: 首页 > news >正文

Pythonweakref与弱引用

Python weakref 与弱引用
==============================

弱引用允许引用对象但不增加引用计数,垃圾回收时可自动回收。
适用于缓存、观察者模式、避免循环引用等场景。

1. ref()——基本弱引用
-------------------------
创建弱引用,对象被回收后弱引用返回 None。

import weakref

class Data:
"""大对象——模拟需要被弱引用的对象"""
def __init__(self, value: str):
self.value = value
self._large_data = [0] * 10_000_000 # 模拟占用大量内存

def __repr__(self):
return f"Data({self.value})"


obj = Data("重要的数据")
ref = weakref.ref(obj) # 创建弱引用

print(ref()) # Data(重要的数据)——通过弱引用访问原对象
print(ref() is obj) # True——是同一个对象

del obj # 删除原对象(唯一强引用)
print(ref()) # None——对象已被回收


2. ref() 回调通知
---------------------
当被引用对象被回收时,回调函数被触发。

class Resource:
"""资源类——回收时通知"""
def __init__(self, name: str):
self.name = name

def __repr__(self):
return f"Resource({self.name})"


def on_delete(ref):
"""回调:当对象被垃圾回收时调用"""
print(f"对象已被回收!弱引用: {ref}")


res = Resource("网络连接")
ref = weakref.ref(res, on_delete) # 注册回调

print(f"对象存活: {ref() is not None}") # True
del res # 触发回调——打印 "对象已被回收!"


3. WeakValueDictionary——缓存
---------------------------------
值被弱引用的字典,对象被回收时自动删除对应条目。
非常适合用作缓存,避免缓存导致内存泄漏。

class UserProfile:
"""用户资料——从数据库加载,放入缓存"""
def __init__(self, user_id: int, name: str):
self.user_id = user_id
self.name = name

def __repr__(self):
return f"UserProfile({self.name})"


class UserCache:
"""用户缓存——使用 WeakValueDictionary 自动管理"""
def __init__(self):
self._cache = weakref.WeakValueDictionary() # 值被弱引用

def get_user(self, user_id: int) -> UserProfile | None:
"""从缓存获取用户,缓存未命中时加载"""
user = self._cache.get(user_id)
if user is None:
# 模拟从数据库加载
user = UserProfile(user_id, f"用户{user_id}")
self._cache[user_id] = user
print(f"从数据库加载 {user}")
else:
print(f"从缓存返回 {user}")
return user


cache = UserCache()
u1 = cache.get_user(1) # 从数据库加载
u2 = cache.get_user(1) # 从缓存返回

del u1 # 删除强引用
# WeakValueDictionary 中的对应条目会自动清除(但延迟回收)
import gc
gc.collect() # 强制垃圾回收
print(cache._cache) # 可能只剩下部分条目


4. WeakKeyDictionary——元数据
----------------------------------
键被弱引用的字典,用于为对象附加元数据而不影响其生命周期。

class Widget:
"""界面组件"""
def __init__(self, name: str):
self.name = name


# 为 Widget 对象附加工具提示信息,但不影响其生命周期
tooltips = weakref.WeakKeyDictionary()

w1 = Widget("按钮")
w2 = Widget("输入框")
tooltips[w1] = "点击此处提交"
tooltips[w2] = "在此输入文本"

print(tooltips[w1]) # "点击此处提交"
del w1 # 删除 Widget 对象
print(len(tooltips)) # 1——w1 的条目自动清除


5. WeakSet——弱引用集合
---------------------------
集合中的元素被弱引用,适合观察者模式。

class Observer:
"""观察者"""
def __init__(self, name: str):
self.name = name

def update(self, message: str) -> None:
print(f"[{self.name}] 收到: {message}")


class Subject:
"""被观察者——使用 WeakSet 持有观察者,避免阻止 GC"""
def __init__(self):
self._observers = weakref.WeakSet() # 观察者被弱引用

def attach(self, observer: Observer) -> None:
self._observers.add(observer)

def notify(self, message: str) -> None:
for obs in self._observers:
obs.update(message)


subject = Subject()
obs1 = Observer("观察者A")
obs2 = Observer("观察者B")

subject.attach(obs1)
subject.attach(obs2)
subject.notify("事件触发!") # 两个观察者都收到

del obs1 # 观察者A 被回收
subject.notify("第二次事件") # 只有观察者B 收到


6. finalize——资源清理
-------------------------
注册对象回收时的清理函数,比 __del__ 更可靠。

import tempfile, os

class TempFile:
"""临时文件——使用 finalize 确保删除"""
def __init__(self, suffix: str = ".tmp"):
self.name = tempfile.mktemp(suffix=suffix)
# 注册清理函数:对象被回收时删除临时文件
weakref.finalize(self, self._cleanup, self.name)
print(f"创建临时文件: {self.name}")

@staticmethod
def _cleanup(path: str) -> None:
"""静态清理函数,不绑定到实例"""
if os.path.exists(path):
os.remove(path)
print(f"删除临时文件: {path}")

def read(self) -> str:
with open(self.name, 'r') as f:
return f.read()


def process_temp():
tf = TempFile()
print(tf.read()) # 使用临时文件
# 函数结束时 tf 被回收,触发 finalize 清理


7. proxy——代理对象
----------------------
proxy 行为像原对象的透明代理,但不需要调用 ref()。

class Service:
def request(self) -> str:
return "服务响应"


svc = Service()
proxy = weakref.proxy(svc) # 创建透明代理

print(proxy.request()) # "服务响应"——无需调用 ()
svc.request is proxy.request # True——方法也相同

del svc
# print(proxy.request()) # ReferenceError: weakly-referenced object no longer exists


总结:weakref 提供了不增加引用计数的对象引用方式,用于缓存
(WeakValueDictionary)、元数据管理(WeakKeyDictionary)、观察者模式
(WeakSet)和资源清理(finalize)。合理使用能有效避免内存泄漏。

http://www.jsqmd.com/news/911679/

相关文章:

  • yuzu模拟器完整教程:免费在PC上玩Switch游戏的终极指南
  • 基于Adafruit CPX与3D打印的智能交互直升机模型制作全攻略
  • [特殊字符] 书匠策AI:你的论文“私人门诊“开张了!教育博主实测全流程科普
  • 三步轻松搞定经典游戏联机:IPXWrapper让老游戏重获新生
  • 5分钟轻松搞定:喜马拉雅VIP音频批量下载神器
  • Lindy智能灌溉控制器深度拆解(固件漏洞/通信协议/边缘逻辑全曝光)
  • 从零打造高扭矩太阳能小车:BO电机并联驱动与纸板结构实践
  • 别再傻傻分不清!工业自动化里零线和地线接错有多危险?附安全接线实操
  • 别再只盯着线路了!PCB层压工艺里的‘棕化’和‘铆合’到底有多重要?
  • C语言新手必看:手把手教你写二进制转十进制的函数(附ZZULIOJ 1142题解)
  • 被97%用户关闭的Lindy隐藏开关,开启后自动拦截92%的BOM错配订单(实测数据+权限配置路径)
  • 最新长期支持版本nodejs安装及环境配置(保姆级图文+安装包)
  • Pythonuvloop性能优化
  • P14076 [GESP202509 六级] 货物运输
  • ​ 带标注的番茄西红柿疾病(黑白图)识别数据集,识别率88.4%,可识别健康叶和7种常见病害,2982张图,支持yolo,coco json,voc xml,文末有模型训练代码
  • 基于ESP32的智能晨间自动化系统:环境感知与物联网实践
  • 从奶茶配方到游戏平衡:正交设计在互联网产品中的那些‘骚操作’
  • 华为ENSP模拟器实战:手把手教你搭建一个带无线AP的校园网(含AC6005配置)
  • 超越基础教程:用iVX的富文本和二维码组件,快速打造一个用户可编辑的内容发布页面
  • 避开理论深坑:手把手调试Buck电源环路,从仿真到实测的避雷指南
  • DHDA框架:动态适应配置性能建模的挑战与解决方案
  • ​ 带标注的番茄西红柿疾病检测数据集,可识别健康和8种常见疾病的叶子,识别率99.1%,8226张图,支持yolo,coco json,voc xml,文末有模型训练代码
  • 别再只跑MS MARCO了!用BEIR基准给你的检索模型做个“零样本体检”(附实战避坑指南)
  • HFSS仿真跑完别急着关!这4个数据后处理结果,帮你判断仿真是否靠谱
  • Lindy项目管理自动化实施倒计时:错过2024Q3窗口期,将面临合规成本激增47%的风险预警
  • Windows Defender的‘小固执’:深入MsMpEng.exe进程,看它为何总不让你的U盘安全弹出
  • 工业设计师的‘秘密武器’:为什么说直接建模才是创意落地的快车道?(附Rhino与Alias案例)
  • 终极ROFL-Player使用指南:快速播放英雄联盟旧版本回放
  • Pythonuuid与唯一标识
  • 从零设计DDR4内存模块:高速PCB与FPGA控制器实战