zoukankan      html  css  js  c++  java
  • weakref:对象的弱引用

    介绍

    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)
    

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

  • 相关阅读:
    Mysql --学习:大量数据快速导入导出
    Mybatis 学习---${ }与#{ }获取输入参数的区别、Foreach的用法
    SSM 框架 ---项目整合
    SMM框架--maven创建web项目
    SSM框架—环境搭建(MyEclipse+Tomcat+MAVEN+SVN)
    Java中 try--catch-- finally、throw、throws 的用法
    Java集合:List、Set和Map的区别,ArrayList和LinkedList有何区别..........
    java中 this() 和super()的作用及用法
    Thread和Runnable的区别和联系、多次start一个线程会怎么样
    vue-devtools google扩展插件安装
  • 原文地址:https://www.cnblogs.com/traditional/p/11870281.html
Copyright © 2011-2022 走看看