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

1. weakref对象的非永久引用

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

1.1 引用

对象的弱引用要通过ref类来管理。要获取原对象,可以调用引用对象。

import weakref

class ExpensiveObject:

    def __del__(self):
        print(‘(Deleting {})‘.format(self))

obj = ExpensiveObject()
r = weakref.ref(obj)

print(‘obj:‘, obj)
print(‘ref:‘, r)
print(‘r():‘, r())

print(‘deleting obj‘)
del obj
print(‘r():‘, r())

在这里,由于obj在第二次调用引用之前已经被删除,所以ref返回None。

1.2 引用回调

ref构造函数接受一个可选的回调函数,删除所引用的对象时会调用这个函数。

import weakref

class ExpensiveObject:

    def __del__(self):
        print(‘(Deleting {})‘.format(self))

def callback(reference):
    """Invoked when referenced object is deleted"""
    print(‘callback({!r})‘.format(reference))

obj = ExpensiveObject()
r = weakref.ref(obj, callback)

print(‘obj:‘, obj)
print(‘ref:‘, r)
print(‘r():‘, r())

print(‘deleting obj‘)
del obj
print(‘r():‘, r())

当引用已经“死亡”而且不再引用原对象时,这个回调会接受这个引用对象作为参数。这个特性的一种用法就是从缓存中删除弱引用对象。

1.3 最终化对象

清理弱引用时要对资源完成更健壮的管理,可以使用finalize将回调与对象关联。finalize实例会一直保留(直到所关联的对象被删除) ,即使应用并没有保留最终化对象的引用。

import weakref

class ExpensiveObject:

    def __del__(self):
        print(‘(Deleting {})‘.format(self))

def on_finalize(*args):
    print(‘on_finalize({!r})‘.format(args))

obj = ExpensiveObject()
weakref.finalize(obj, on_finalize, ‘extra argument‘)

del obj

finalize的参数包括要跟踪的对象,对象被垃圾回收时要调用的callable,以及传入这个callable的所有位置或命名参数。

这个finalize实例有一个可写属性atexit,用来控制程序退出时是否调用这个回调(如果还未调用)。

import sys
import weakref

class ExpensiveObject:

    def __del__(self):
        print(‘(Deleting {})‘.format(self))

def on_finalize(*args):
    print(‘on_finalize({!r})‘.format(args))

obj = ExpensiveObject()
f = weakref.finalize(obj, on_finalize, ‘extra argument‘)
f.atexit = bool(int(sys.argv[1]))

默认设置是调用这个回调。将atexit设置为false会禁用这种行为。

如果向finalize实例提供所跟踪对象的一个引用,这便会导致一个引用被保留,所以这个对象永远不会被垃圾回收。

import gc
import weakref

class ExpensiveObject:

    def __del__(self):
        print(‘(Deleting {})‘.format(self))

def on_finalize(*args):
    print(‘on_finalize({!r})‘.format(args))

obj = ExpensiveObject()
obj_id = id(obj)

f = weakref.finalize(obj, on_finalize, obj)
f.atexit = False

del obj

for o in gc.get_objects():
    if id(o) == obj_id:
        print(‘found uncollected object in gc‘)

如上所示,尽管obj的显式引用已经删除,但是这个对象仍保留,通过f对垃圾回收器可见。

使用所跟踪对象的一个绑定方法作为callable也可以适当地避免对象最终化。

import gc
import weakref

class ExpensiveObject:

    def __del__(self):
        print(‘(Deleting {})‘.format(self))

    def do_finalize(self):
        print(‘do_finalize‘)

obj = ExpensiveObject()
obj_id = id(obj)

f = weakref.finalize(obj, obj.do_finalize)
f.atexit = False

del obj

for o in gc.get_objects():
    if id(o) == obj_id:
        print(‘found uncollected object in gc‘)

由于为finalize提供的callable是实例obj的一个绑定方法,所以最终化方法保留了obj的一个引用,它不能被删除和被垃圾回收。

1.4 代理

有时使用代理比较弱引用更方便。使用代理可以像使用原对象一样,而且不要求在访问对象之前先调用代理。这说明,可以将代理传递到一个库,而这个库并不知道它接收的是一个引用而不是真正的对象。

import weakref

class ExpensiveObject:

    def __init__(self, name):
        self.name = name

    def __del__(self):
        print(‘(Deleting {})‘.format(self))

obj = ExpensiveObject(‘My Object‘)
r = weakref.ref(obj)
p = weakref.proxy(obj)

print(‘via obj:‘, obj.name)
print(‘via ref:‘, r().name)
print(‘via proxy:‘, p.name)
del obj
print(‘via proxy:‘, p.name)

如果引用对象被删除后再访问代理,会产生一个ReferenceError异常。

1.5 缓存对象

ref和proxy类被认为是“底层”的。尽管它们对于维护单个对象的弱引用很有用,并且还支持对循环引用的垃圾回收,但WeakKeyDictionary和WeakValueDictionary类为创建多个对象的缓存提供了一个更适合的API。

WeakValueDictionary类使用它包含的值的弱引用,当其他代码不再真正使用这些值时,则允许垃圾回收。利用垃圾回收器的显式调用,下面展示了使用常规字典和WeakValueDictionary完成内存处理的区别。

import gc
from pprint import pprint
import weakref

gc.set_debug(gc.DEBUG_UNCOLLECTABLE)

class ExpensiveObject:

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return ‘ExpensiveObject({})‘.format(self.name)

    def __del__(self):
        print(‘    (Deleting {})‘.format(self))

def demo(cache_factory):
    # hold objects so any weak references
    # are not removed immediately
    all_refs = {}
    # create the cache using the factory
    print(‘CACHE TYPE:‘, cache_factory)
    cache = cache_factory()
    for name in [‘one‘, ‘two‘, ‘three‘]:
        o = ExpensiveObject(name)
        cache[name] = o
        all_refs[name] = o
        del o  # decref

    print(‘  all_refs =‘, end=‘ ‘)
    pprint(all_refs)
    print(‘\n  Before, cache contains:‘, list(cache.keys()))
    for name, value in cache.items():
        print(‘    {} = {}‘.format(name, value))
        del value  # decref

    # remove all references to the objects except the cache
    print(‘\n  Cleanup:‘)
    del all_refs
    gc.collect()

    print(‘\n  After, cache contains:‘, list(cache.keys()))
    for name, value in cache.items():
        print(‘    {} = {}‘.format(name, value))
    print(‘  demo returning‘)
    return

demo(dict)
print()

demo(weakref.WeakValueDictionary)

如果循环变量指示所缓存的值,那么这些循环变量必须被显式清除,以使对象的引用数减少。否则,垃圾回收器不会删除这些对象,它们仍然会保留在缓存中。类似地,all_refs变量用来保存引用,以防止它们被过早地垃圾回收。

WeakKeyDictionary的工作与之类似,不过使用了字典中键的弱引用而不是值的弱引用。

原文地址:https://www.cnblogs.com/liuhui0308/p/12346682.html

时间: 2024-10-07 15:31:13

Python3标准库:weakref对象的非永久引用的相关文章

4.Python3标准库--算法

(一)functools:管理函数的工具 import functools ''' functools模块提供了一些工具来管理或扩展和其他callable对象,从而不必完全重写 ''' 1.修饰符 from functools import partial ''' functools模块提供的主要工具就是partial类,可以用来包装一个有默认参数的callable对象. 得到的对象本身就是callable,可以把它看作是原来的参数. ''' # 举个栗子 def foo(name, age,

Python3标准库:copy复制对象

1. copy复制对象 copy模块包括两个函数copy()和deepcopy(),用于复制现有的对象. 1.1 浅副本 copy()创建的浅副本(shallow copy)是一个新容器,其中填充了原对象内容的引用.建立list对象的一个浅副本时,会构造一个新的list,并将原对象的元素追加到这个list. import copy import functools @functools.total_ordering class MyClass: def __init__(self, name):

Python3标准库:shelve对象的持久存储

1. shelve对象的持久存储 不需要关系数据库时,可以用shelve模块作为持久存储Python对象的一个简单的选择.类似于字典,shelf按键访问.值将被pickled并写至由dbm创建和管理的数据库. 1.1 创建一个新shelf 使用shelve最简单的方法就是利用DbfilenameShelf类.它使用dbm存储数据.这个类可以直接使用,也可以通过调用shelve.open()来使用. import shelve with shelve.open('test_shelf.db') a

Python3标准库

文本 1. string:通用字符串操作 2. re:正则表达式操作 3. difflib:差异计算工具 4. textwrap:文本填充 5. unicodedata:Unicode字符数据库 6. stringprep:互联网字符串准备工具 7. readline:GNU按行读取接口 8. rlcompleter:GNU按行读取的实现函数 二进制数据 9. struct:将字节解析为打包的二进制数据 10. codecs:注册表与基类的编解码器 数据类型 11. datetime:基于日期与

Python3标准库:urllib.parse分解URL

1. urllib.parse分解URL urllib.parse模块提供了一些函数,可以管理URL及其组成部分,这包括将URL分解为组成部分以及由组成部分构成URL. 1.1 解析 urlparse()函数的返回值是一个ParseResult对象,其相当于一个包含6个元素的tuple. from urllib.parse import urlparse url = 'http://netloc/path;param?query=arg#frag' parsed = urlparse(url)

Python3标准库:hashlib密码散列

1. hashlib密码散列 hashlib模块定义了一个API来访问不同的密码散列算法.要使用一个特定的散列算法,可以用适当的构造器函数或new()来创建一个散列对象.不论使用哪个具体的算法,这些对象都使用相同的API. 1.1 散列算法 由于hashlib有OpenSSL提供“底层支持”,所以OpenSSL库提供的所有算法都可用,包括: md5 sha1 sha224 sha256 sha384 sha512 有些算法在所有平台上都可用,而有些则依赖于底层库.这两种算法分别由algorith

Python3标准库:threading进程中管理并发操作

1. threading进程中管理并发操作 threading模块提供了管理多个线程执行的API,允许程序在同一个进程空间并发的运行多个操作. 1.1 Thread对象 要使用Thread,最简单的方法就是用一个目标函数实例化一个Thread对象,并调用start()让它开始工作. import threading def worker(): """thread worker function""" print('Worker') threads

Python3标准库:selectors I/O多路复用抽象

1. selectors I/O多路复用抽象 selectors模块在select中平台特定的I/O监视函数之上提供了一个平台独立的抽象层. 1.1 操作模型 selectors中的API是基于事件的,与select中的poll()类似.它有多个实现,并且这个模块会自动设置别名DefaultSelector来指示对当前系统配置最为高效的一个实现.选择器对象提供了一些方法,可以指定在一个套接字上查找哪些事件,然后以一种平台独立的方式让调用者等待事件.注册对事件的兴趣会创建一个SelectorKey

Python学习笔记12:标准库之对象序列化(pickle包,cPickle包)

计算机的内存中存储的是二进制的序列. 我们能够直接将某个对象所相应位置的数据抓取下来,转换成文本流 (这个过程叫做serialize),然后将文本流存入到文件里. 因为Python在创建对象时,要參考对象的类定义,所以当我们从文本中读取对象时,必须在手边要有该对象的类定义,才干懂得怎样去重建这一对象. 从文件读取时,对于Python的内建(built-in)对象 (比方说整数.词典.表等等),因为其类定义已经加载内存,所以不须要我们再在程序中定义类. 但对于用户自行定义的对象,就必需要先定义类,