Skip to content

Latest commit

 

History

History
183 lines (137 loc) · 6.01 KB

弱引用.md

File metadata and controls

183 lines (137 loc) · 6.01 KB

前言

正是因为有引用,对象才会在内存中存在。当对象的引用数量归零后,垃圾回收程序会把对象销毁。如果我们使用一个全局变量引用了一个对象,那直到程序退出前,对象的内存都不会被释放掉。此时,我们可以使用弱引用。什么是弱引用?弱引用不会增加对象的引用数量,引用的目标对象称为所指对象(referent)。因此我们说,弱引用不会妨碍所指对象被当作垃圾回收

使用弱引用

import weakref

_ = {0, 1}
wref = weakref.ref(_)
print(wref())       # {0, 1}
_ = {2,3,4}
print(wref())       # None

我们可以看到,原来 wref 引用的是 {0, 1}。但是, 当 _ 指向 {2, 3, 4} 之后,{0, 1} 不再有对象引用它了,所以它被垃圾回收了,导致之后输出 wref() 时为 None。

弱引用在缓存应用中很有用,因为我们不想仅因为被缓存引用着而始终保存缓存对象。

WeakValueDictionary

WeakValueDictionary类实现的是一种可变映射,里面的值是对象的弱引用。被引用的对象在程序中的其他地方被当作垃圾回收后,对应的键会自动从WeakValueDictionary中删除。因此,WeakValueDictionary经常用于缓存。

>>> import weakref
>>> class Student:
...     def __init__(self, name):
...         self.name = name
...     def __repr__(self):
...         return f'{Student}self.name'
...
>>> stock = weakref.WeakValueDictionary()
>>> s_list = [Student("Ming"), Student("Hong"), Student("Gang")]
>>> for student in s_list:		# 临时变量student引用了对象,这可能会导致该变量的存在时间比预期长。
...     stock[s.name] = student
...
>>> sorted(stock.keys())
['Gang', 'Hong', 'Ming']
>>> del s_list				# 删除引用了三个 Student 的list
>>> sorted(stock.keys())	# student最后引用的是"Gang"。
['Gang']
>>> del student				# for循环中的变量student是全局变量,除非显式删除,否则不会消失。
>>> sorted(stock.keys())
[]

WeakSet

weakref模块还提供了WeakSet类,按照文档的说明,这个类的作用很简单:“保存元素弱引用的集合类。元素没有强引用时,集合会把它删除。”如果一个类需要知道所有实例,一种好的方案是创建一个WeakSet类型的类属性,保存实例的引用。如果使用常规的set,实例永远不会被垃圾回收,因为类中有实例的强引用,而类存在的时间与Python进程一样长,除非显式删除类。

我们来看看实例:

"""
如果一个类需要知道所有实例,一种好的方案是创建一个WeakSet类型的类属性,保存实例的引用。如果使用常规的set,实例永远不会被垃圾回收,因为类中有实例的强引用,而类存在的时间与Python进程一样长,除非显式删除类。
"""
import weakref

class Student:
    stock_set = weakref.WeakSet()

    def __new__(cls, *args, **kwargs):
        new_one = super().__new__(cls)  # 调用父类object初始化
        cls.stock_set.add(new_one)  # 存储实例对象的弱引用
        return new_one

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

    def func(self):
        print(self.name)


def test():
    s1, s2 = Student("XiaoMing"), Student("XiaoWang")

    print(Student.stock_set.data)
    # {<weakref at 0x0000020667B6E1D8; to 'Student' at 0x0000020676CA67C8>, <weakref at 0x0000020667B6E2C8; to 'Student' at 0x0000020676CA6748>}
    
    for ws in Student.stock_set:
        ws.func()  # 打印 XiaoMing XiaoWang

    weak_one = Student.stock_set.pop()  # pop 一个弱引用
    weak_one.func()  # 打印 XiaoMing

test()  # 函数结束之后 两个 Student 实例对象成功被释放掉了
print(Student.stock_set.data)  # 打印 set()

输出:

{<weakref at 0x0000020667B6E1D8; to 'Student' at 0x0000020676CA67C8>, <weakref at 0x0000020667B6E2C8; to 'Student' at 0x0000020676CA6748>}
XiaoWang
XiaoMing
XiaoWang
set()

我们看看这里使用 WeakValueDictionary():

import weakref

class Student:
    stock = weakref.WeakValueDictionary()
    def __new__(cls, *args, **kwargs):
        new_one = super().__new__(cls)	# 调用父类object初始化
        cls.stock[args[0]] = new_one    # 以 name 为键,值为实例对象的弱引用
        return new_one

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

    def func(self):
        print(self.name)

def test():
    s1, s2 = Student("XiaoMing"), Student("XiaoWang")
    print(Student.stock.keys())     # Student.stock.keys() 实际上是一个生成器对象
    print(sorted(Student.stock.keys()))     # 使用sorted()输出

    weak_xiaoming = Student.stock["XiaoMing"]
    weak_xiaoming.func()

    for k, v in Student.stock.items():	# k为name,v为弱引用
        print(k, v)
        # XiaoMing <__main__.Student object at 0x0000020676CA6748>
        # XiaoWang <__main__.Student object at 0x0000020676CA67C8>

test()	# 函数结束之后 两个 Student 实例对象成功被释放掉了
print(sorted(Student.stock.keys()))     # 打印 []

输出:

<generator object WeakValueDictionary.keys at 0x00000206678FEF48>
['XiaoMing', 'XiaoWang']
XiaoMing
XiaoMing <__main__.Student object at 0x0000020676CA6748>
XiaoWang <__main__.Student object at 0x0000020676CA67C8>
[]

还有一个WeakKeyDictionary,其键是弱引用。(WeakKeyDictionary实例)可以为应用中其他部分拥有的对象附加数据,这样就无需为对象添加属性。这对覆盖属性访问权限的对象尤其有用。

import weakref

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

    def func(self):
        print(self.name)

    def __repr__(self):
        return f'{Student}self.name'

s1, s2 = Student("XiaoMing"), Student("XiaoWang")
stock = weakref.WeakKeyDictionary()
stock[s1] = {"Class": "A1"}
stock[s2] = {"Class": "B2"}

print("Before del")
for i in stock.values():
    print(i)

del s1, s2
print("After del")
for i in stock.values():
    print(i)
Before del
{'Class': 'A1'}
{'Class': 'B2'}
After del