• 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('
      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('
      Cleanup:')
        del all_refs
        gc.collect()
    
        print('
      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的工作与之类似,不过使用了字典中键的弱引用而不是值的弱引用。

  • 相关阅读:
    评论拷贝JS写怀旧小游戏系列(六)躲人游戏
    宋体生成mongodb 聚合函数
    数据数字mongodb 模糊查询以及$type使用
    三国索引oracle 全文索引优化like
    插件网页怎么下载安装Firebug和使用Firebug
    对象调用javascript 中强制执行 toString()
    清空数据Android 跳转到一个应用安装的详情界面的方法
    抽样概率machine learning sampling 采样
    判断类型JS写怀旧小游戏系列(七)吃方块
    文档项目程序人生2009年(55)
  • 原文地址:https://www.cnblogs.com/liuhui0308/p/12346682.html
Copyright © 2020-2023  润新知