介绍

weakref支持对象的弱引用,正常的引用会增加对象的引用计数,并避免它被垃圾回收。但结果并不是总和期望的那样,比如有时候可能会出现一个循环引用,或者有时候需要内存时可能要删除对象的缓存。而弱引用(weak reference)是一个不会增加引用计数的对象句柄

引用

import weakref

'''
对象的弱引用要通过ref类来管理。要获取原对象,可以调用引用对象
''' class RefObject:
def __del__(self):
print("del executed") obj = RefObject()
# 创建弱引用
r = weakref.ref(obj) print("obj:", obj) # obj: <__main__.RefObject object at 0x0000000002964470>
# 显示关联RefObject
print("ref:", r) # ref: <weakref at 0x000000000051BA48; to 'RefObject' at 0x0000000002964470> # 引用r加上(),等价于obj,因此得到RefObject的实例对象
print("ref()", r()) # ref() <__main__.RefObject object at 0x0000000002964470> # 删除obj执行析构函数
del obj # del executed
# 之前说过调用r()等价于调用obj,但是obj被删除了,所以返回None
# 从这里返回None也能看出这个弱引用是不会增加引用计数的
print("r():", r()) # r(): None

引用回调

import weakref

'''
ref构造函数可以接受一个可选的回调函数,删除引用所指向的对象时就会调用这个回调函数
''' class RefObject:
def __del__(self):
print("del executed") def callback(reference):
print(f"callback : {reference}") obj = RefObject()
r = weakref.ref(obj, callback)
'''
当引用所引用的原对象"死亡"时,这个回调会接受这个引用对象作为参数。
这种特性的一种用法就是从缓存中删除弱引用对象。
''' print("obj:", obj) # obj: <__main__.RefObject object at 0x0000000002964630>
print("ref:", r) # ref: <weakref at 0x0000000001D2BA48; to 'RefObject' at 0x0000000002964630>
print("ref()", r()) # ref() <__main__.RefObject object at 0x0000000002964630> del obj # 删除引用指向的对象
"""
del executed
callback : <weakref at 0x0000000001D2BA48; dead> 删除obj,执行回调,显示dead
"""
print("r():", r()) # r(): None

最终化对象

import weakref

'''
清理弱引用时要对资源完成更健壮的管理,可以使用finalize将回调与对象关联。
finalize实例会一直保留(直到所关联的对象被删除),即使没有保留最终化对象的引用
''' class RefObj:
def __del__(self):
print("xxx") def on_finalize(*args):
print(f"on_finalize: {args}") obj = RefObj()
weakref.finalize(obj, on_finalize, "callback的参数") del obj
'''
xxx
on_finalize: ('callback的参数',)
'''
# finalize的参数包括要跟踪的对象,对象被垃圾回收时要调用的callback,以及参数(可以是位置参数,也可以是关键字参数) # finalize实例对象还有一个atexit属性,用来控制程序退出时是否调用这个回调(如果还未调用)
obj1 = RefObj()
f = weakref.finalize(obj1, on_finalize, "callback的参数")
# 默认是调用回调,但是将atexit设置为False会禁用这种行为
f.atexit = False
'''
不会有任何的输出,注意:这里我虽然没有显示的删除obj1,但也能够说明结论
因为在f.atexit=True的情况下,即使不删除也依旧会执行callback。
原因是即使你不手动删除,但是对象已经被创建出来了,而程序结束的那一刻,也会执行析构函数的。因为对象总是要回收的,即使你不调用del,那么程序执行完毕的时候也会自动调用。
所以默认f.atexit = True是会打印的,但是现在没有打印,所以确实被禁用了
'''
import weakref

'''
如果向finalize实例提供一个跟踪对象的引用,这便会导致一个引用被保留,所以这个对象永远不会被垃圾回收
''' class RefObj:
def __del__(self):
print("xxx") def on_finalize(*args):
print(f"on_finalize: {args}") obj = RefObj()
obj_id = id(obj)
# 这里我将obj实例作为参数传进去了,这样的后果就是obj不会被回收,即使你删除了
f = weakref.finalize(obj, on_finalize, obj)
f.atexit = False # 删除obj,让obj不再指向之前的对象
del obj
import gc
# 获取所有的对象
for o in gc.get_objects():
if id(o) == obj_id:
# 结果发现真的没有被回收,因为引用不止obj一个,还有其它人在用
print("found uncollected object in gc") # found uncollected object in gc

代理

import weakref

'''
有时候使用代理比使用弱引用更方便。使用代理可以像使用原对象一样,而且不要求在访问对象之前先调用代理。
这说明,可以将代理传递到一个库,而这个库并不知道它接收的是一个代理而不是真正的一个对象。
''' class RefObj: def __init__(self, name):
self.name = name def __del__(self):
print("xxx") obj = RefObj("my obj")
r = weakref.ref(obj)
p = weakref.proxy(obj) # 可以看到引用加上()才相当于原来的对象
# 而代理不需要,直接和原来的对象保持一致
print("via obj:", obj.name) # via obj: my obj
print("via ref:", r().name) # via ref: my obj
print("via proxy:", p.name) # via proxy: my obj del obj # xxx
try:
# 删除对象之后,再调用引用,打印为None
print(r()) # None
# 但是如果调用代理的话,则会抛出一个ReferenceError
print(p)
except Exception as e:
print(e) # weakly-referenced object no longer exists

自定义类指定弱引用

当我们自定义一个类的时候,如果为了省内存,那么会不使用__dict__属性,因为每一个类或者实例都会有一个自己的属性字典__dict__,而我们知道字典使用的是哈希表,这是一个使用空间换时间的数据结构,因此如果想省内存的话,那么我们通常的做法是指定__slots__属性,这样就不会再有__dict__属性了。

class A:
__slots__ = ('name', 'age') def __init__(self, name, age):
# 此时在__init__里面,只能有self.name和self.age
# 这是因为我们在__slots__里面只指定了name和age
# 因此当我们需要省内存、并且属性固定的时候,可以指定__slots__属性
self.name = name
self.age = age def __str__(self):
return f"name is {self.name}, age is {self.age}" if __name__ == '__main__':
import weakref
a = A("hanser", 27)
try:
r = weakref.proxy(a)
except TypeError as e:
print(e) # cannot create weak reference to 'A' object

但是我们发现此时这个A的实例对象是没有办法被弱引用的,因为我们指定了__slots__,那么要怎么做呢?直接在__slots__里面加上一个属性就好了。

class A:
# 多指定一个__weakref__,表示支持弱引用
__slots__ = ('name', 'age', '__weakref__') def __init__(self, name, age):
self.name = name
self.age = age def __str__(self):
return f"name is {self.name}, age is {self.age}" if __name__ == '__main__':
import weakref
a = A("hanser", 27)
r = weakref.proxy(a)
print(r)

可以看到此时就支持弱引用了。

weakref:对象的弱引用的更多相关文章

  1. C#中考虑为大对象使用弱引用

    1.无论怎样尽力,我们总是会使用到某些需要大量内存的数据,而这些内存并不需要经常访问.或许你需要从一个大文件中查找某个特定的值,或者算法需要一个较大的查询表.这时,你也许会采用2中不太好做法:第一种是 ...

  2. [翻译] 编写高性能 .NET 代码--第二章 GC -- 减少大对象堆的碎片,在某些情况下强制执行完整GC,按需压缩大对象堆,在GC前收到消息通知,使用弱引用缓存对象

    减少大对象堆的碎片 如果不能完全避免大对象堆的分配,则要尽量避免碎片化. 对于LOH不小心就会有无限增长,但LOH使用的空闲列表机制可以减轻增长的影响.利用这个空闲列表,我们可以在两块分配区域中间找到 ...

  3. Python3标准库:weakref对象的非永久引用

    1. weakref对象的非永久引用 weakref模块支持对象的弱引用.正常的引用会增加对象的引用数,并避免它被垃圾回收.但结果并不总是如期望中的那样,比如有时可能会出现一个循环引用,或者有时需要内 ...

  4. java对象的四种引用:强引用、软引用、弱引用和虚引用

    在JDK1.2之前,创建的对象只有在处于可触及(reachable)的状态下,才能被程序使用.也就是说,若一个对象不被任何变量引用,那么程序就无法再使用这个对象.垃圾回收器一旦发现这些无用对象,就会对 ...

  5. java对象的强引用,软引用,弱引用和虚引用

    1.强引用 以前我们使用的大部分引用实际上都是强引用,这是使用最普遍的引用.如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它.当内存空 间不足,Java虚拟机宁愿抛出Out ...

  6. 谈谈Java对象的强引用,软引用,弱引用,虚引用分别是什么

    整体结构 java提供了4中引用类型,在垃圾回收的时候,都有自己的各自特点. 为什么要区分这么多引用呢,其实这和Java的GC有密切关系. 强引用(默认支持模式) 把一个对象赋给一个引用变量,这个引用 ...

  7. 合理使用软引用和弱引用,提升JVM内存使用性能

    在项目运行时,OOM异常是比较处理的,因为从日志看出的发生异常的代码点可能仅仅是最后一根稻草,从中可能未必能发现OOM的原因,而且OOM未必是固定重现的. 上医治未病,与其等OOM问题发生时再通过看日 ...

  8. 通过软引用和弱引用提升JVM内存使用性能的方法(面试时找机会说出,一定能提升成功率)

    初学者或初级程序员在面试时如果能证明自己具有分析内存用量和内存调优的能力,这相当有利,因为这是针对5年左右相关经验的高级程序员的要求.而对于高级程序员来说,如果能在面试时让面试官感觉你确实做过内存调优 ...

  9. 你不可不知的Java引用类型之——弱引用

    定义 弱引用是使用WeakReference创建的引用,弱引用也是用来描述非必需对象的,它是比软引用更弱的引用类型.在发生GC时,只要发现弱引用,不管系统堆空间是否足够,都会将对象进行回收. 说明 弱 ...

随机推荐

  1. 使用nginx实现动静分离的负载均衡集群

    一.概述: LB负载均衡集群分两类: LVS (四层)和 nginx或haproxy (七层) 客户端通过访问分发器的VIP来访问网站 |现在应用更复杂,比如现在网站页面有: .php .html . ...

  2. RxJS 6有哪些新变化?

    我们的前端工程由Angular4升级到Angular6,rxjs也要升级到rxjs6.  rxjs6的语法做了很大的改动,幸亏引入了rxjs-compact包,否则升级工作会无法按时完成. 按照官方的 ...

  3. Linux命令集锦:scp命令

    scp命令用于在Linux下进行远程拷贝文件的命令,和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,而且scp传输是加密的,可能会稍微影响一下速度.当你服务器硬盘变为只读 read on ...

  4. Go项目实战:打造高并发日志采集系统(一)

    项目结构 本系列文章意在记录如何搭建一个高可用的日志采集系统,实际项目中会有多个日志文件分布在服务器各个文件夹,这些日志记录了不同的功能.随着业务的增多,日志文件也再增多,企业中常常需要实现一个独立的 ...

  5. ppt学习笔记

    文档:ppt学习笔记.note链接:http://note.youdao.com/noteshare?id=719a525ca3420e3692b1025d5d904c02&sub=4E52E ...

  6. SqlServer:SqlServer(服务器磁盘监控,创建管理员账号分配权,添加链接服务器,查询CPU,查询内存)

    1.服务器磁盘监控 (1)总链接服务上开启所有链接服务器的RPC: ----------------------总链接服务器上面,开启每个服务器的RPC --exec sp_serveroption ...

  7. 什么是vue生命周期和生命周期钩子函数?

    原文地址 vue生命周期简介 咱们从上图可以很明显的看出现在vue2.0都包括了哪些生命周期的函数了. 生命周期探究 对于执行顺序和什么时候执行,看上面两个图基本有个了解了.下面我们将结合代码去看看钩 ...

  8. 【无线安全实践入门】破解WiFi密码的多个方法

    本文希望可以帮助到想要学习接触此方面.或兴趣使然的你,让你有个大概的印象. 文中可能存在错误操作或错误理解,望大家不吝指正. !阅前须知! 本文是基于我几年前的一本笔记本,上面记录了我学习网络基础时的 ...

  9. hive的hiveserver2模式启动不起来,发现Hadoop一直处于安全模式

    hive的hiveserver2模式启动不起来,发现Hadoop一直处于安全模式 命令介绍 命令hadoop fs –safemode get 查看安全模式状态 命令hadoop fs –safemo ...

  10. 【LOJ】#2983. 「WC2019」数树

    LOJ2983. 「WC2019」数树 task0 有\(i\)条边一样答案就是\(y^{n - i}\) task1 这里有个避免容斥的方法,如果有\(i\)条边重复我们要算的是\(y^{n - i ...