python--基础知识点--弱引用

1.概念

弱引用:不会增加对象的引用数量,不会妨碍所指对象(referent)被当作垃圾回收。弱引用可用于解决循环引用的问题。

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

在Python中,使用弱引用(weak reference)主要有以下几个场景:

  • 缓存:当需要缓存对象但不希望这些对象被缓存所阻止垃圾回收时,可以使用弱引用。弱引用允许垃圾回收器在没有其他引用的情况下自动删除对象。这在需要实现缓存的场景中非常有用。
  • 监听器模式:当一个对象需要监听另一个对象的状态变化,但不需要保持对被监听对象的强引用时,可以使用弱引用。这样,在被监听对象被垃圾回收时,监听器对象也可以自动被释放。
  • 对象关系映射(ORM):在ORM框架中,为了避免循环引用和内存泄漏,通常会使用弱引用来处理对象之间的关系。比如,一个父对象包含对子对象的引用,而子对象也包含对父对象的引用,为了防止循环引用导致无法释放内存,可以使用弱引用来保存对父对象的引用。
  • 临时引用:当需要在某些情况下暂时引用一个对象,但又不希望该引用影响到垃圾回收的行为时,可以使用弱引用。这样,在没有其他强引用的情况下,对象可以被垃圾回收。

需要注意的是,弱引用对象并不能保证其引用的对象一定会被垃圾回收。它只是提供了一种机制,使垃圾回收器能够在没有其他强引用的情况下自动删除对象。垃圾回收的具体时机仍然取决于垃圾回收器的策略和算法。

2.弱引用的创建
2.1创建弱引用

通过调用 weakref 模块的 ref(obj[,callback]) 来创建一个弱引用,obj 是你想弱引用的对象, callback 是一个可选的函数,当因没有引用导致 Python 要销毁这个对象时调用。回调函数callback要求单个参数(弱引用的对象)。

一旦你有了一个对象的弱引用,你就能通过调用弱引用来获取被弱引用的对象。

>>> import sys
>>> import weakref
>>> class Man():
...     def __init__(self, name):
...             self.name = name
...
>>> man0 = Man('zhe')    # 增加一个引用  count = 1 
>>> sys.getrefcount(man0)
2
>>> r = weakref.ref(man0)   # 增加一个弱引用  count = 1  
>>> sys.getrefcount(man0)
2
>>> r   # 获取弱引用所指向的对象
<weakref at 0x0000026AF3974688; to 'Man' at 0x0000026AF398C710>
>>> man1 = r()
>>> man0 is man1
True
>>> sys.getrefcount(man0)
3
>>> man0 = None
>>> man1 = None
>>> r   # 当对象引用计数为零时,弱引用失效。
<weakref at 0x0000026AF3974688; dead>

weakref.ref 类的实例获取所指对象,提供的是底层接口,尽量不要手动创建并处理weakref.ref实例。

注:

  • 上面的代码中,我们使用 sys 包中的 getrefcount() 来查看某个对象的引用计数。需要注意的是,当使用某个引用作为参数,传递给 getrefcount() 时,参数实际上创建了一个临时的引用。因此, getrefcount() 所得到的结果,会比期望的多 1 。
  • 一旦没有了对这个对象的其它的引用,调用弱引用将返回None,因为Python已经销毁了这个对象。 注意:大部分的对象不能通过弱引用来访问。
  • weakref 模块中的 getweakrefcount(obj) 和 getweakrefs(obj) 分别返回弱引用数和关于所给对象的引用列表。
  • 弱引用对于创建对象(这些对象很费资源)的缓存是有用的。
  • 对弱引用的使用来访问原对象要通过 weakref() 的形式。 其实可以创建代理对象对原对象进行访问。
2.2创建代理对象

代理对象 是弱引用对象,它们的行为就像它们所引用的对象,这就便于你不必首先调用弱引用来访问背后的对象。通过weakref模块的proxy(obj[,callback])函数来创建代理对象。使用代理对象就如同使用对象本身一样:

相比于创建弱引用 在调用上完全等同于

>>> import sys
>>> import weakref
>>> class Man():
...     def __init__(self, name):
...             self.name = name
...
>>> def callback_ref(self):
...     print (self)
...     print ("callback_ref")
...
>>> def callback_proxy(self):
...     print (self)
...     print ("callback_proxy")
...
>>> man = Man('zhe')   # 引用计数 +1
>>> sys.getrefcount(man)
2
>>> man_ref = weakref.ref(man, callback_ref)  # 弱引用 引用计数不变
>>> sys.getrefcount(man)
2
>>> man_ref   # 弱引用对象
<weakref at 0x0000019A63664638; to 'Man' at 0x0000019A6367C668>
>>> man_ref.name   # 对原对象的访问形式错误
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'weakref' object has no attribute 'name'
>>> man_ref().name    # 正确的对原对象的访问形式
'zhe'
>>> man_proxy = weakref.proxy(man, callback_proxy)   # 使用代理 引用计数不变
>>> sys.getrefcount(man)
2
>>> man_proxy  # 代理对象
<weakproxy at 0x0000019A634D6BD8 to Man at 0x0000019A6367C668>
>>> man_proxy.name   # 访问形式与原引用相同
'zhe'
>>> del man   # 注
Exception ignored in: <function callback_proxy at 0x0000019A636807B8>
Traceback (most recent call last):
  File "<stdin>", line 2, in callback_proxy
ReferenceError: weakly-referenced object no longer exists
<weakref at 0x0000019A63664638; dead>
callback_ref

2.3缓存对象

一般不要直接创建处理 weakref.ref 实例,最好使用 weakref 集合和 finalize,即 WeakKeyDictionary、WeakValueDictionary、WeakSet、finalize。

2.3.1 WeakValueDictionary

WeakValueDictionary 是Python标准库中weakref模块提供的一个类,它是一种特殊的字典,它的值是弱引用对象。这意味着,如果一个值对象在程序中没有其他引用,那么它可以被垃圾回收并从字典中自动删除。

以下是WeakValueDictionary的使用示例:

import weakref

# 创建一个WeakValueDictionary对象
d = weakref.WeakValueDictionary()

# 定义一个类
class MyClass:
    def __init__(self, name):
        self.name = name

# 创建实例对象,并将其作为值添加到字典中
obj1 = MyClass('Object 1')
obj2 = MyClass('Object 2')
d['key1'] = obj1
d['key2'] = obj2

# 输出字典的内容
print(d)  # 输出: {'key1': <__main__.MyClass object at 0x...>, 'key2': <__main__.MyClass object at 0x...>}

# 删除原始对象的引用
del obj1

# 等待垃圾回收

# 输出字典的内容
print(d)  # 输出: {'key2': <__main__.MyClass object at 0x...>}

在这个示例中,我们创建了一个WeakValueDictionary对象d。然后,我们定义了一个MyClass类,并创建了两个实例对象obj1和obj2。我们将这两个对象作为值添加到字典d中。

当我们删除obj1的引用之后,obj1没有其他引用了,它可以被垃圾回收。由于d中的值是弱引用对象,当obj1被垃圾回收时,对应的键值对’key1’: obj1也会从字典中自动删除。

因此,在等待垃圾回收之后,我们可以看到字典中只剩下’key2’: obj2这个键值对。这表明WeakValueDictionary将自动处理那些不再有其他引用的值对象的删除。

2.3.2 WeakKeyDictionary

WeakKeyDictionary是Python标准库中weakref模块提供的另一个类,它是一种特殊的字典,它的键是弱引用对象。这意味着,如果一个键对象在程序中没有其他引用,那么它可以被垃圾回收并从字典中自动删除。

以下是WeakKeyDictionary的使用示例:

import weakref

# 创建一个WeakKeyDictionary对象
d = weakref.WeakKeyDictionary()

# 定义一个类
class MyClass:
    def __init__(self, name):
        self.name = name

# 创建实例对象,并将其作为键添加到字典中
obj1 = MyClass('Object 1')
obj2 = MyClass('Object 2')
d[obj1] = 'Value 1'
d[obj2] = 'Value 2'

# 输出字典的内容
print(d)  # 输出: {<__main__.MyClass object at 0x...>: 'Value 1', <__main__.MyClass object at 0x...>: 'Value 2'}

# 删除原始对象的引用
del obj1

# 等待垃圾回收

# 输出字典的内容
print(d)  # 输出: {<__main__.MyClass object at 0x...>: 'Value 2'}

在这个示例中,我们创建了一个WeakKeyDictionary对象d。然后,我们定义了一个MyClass类,并创建了两个实例对象obj1和obj2。我们将这两个对象作为键添加到字典d中。

当我们删除obj1的引用之后,obj1没有其他引用了,它可以被垃圾回收。由于d中的键是弱引用对象,当obj1被垃圾回收时,对应的键值对obj1: 'Value 1’也会从字典中自动删除。

因此,在等待垃圾回收之后,我们可以看到字典中只剩下obj2: 'Value 2’这个键值对。这表明WeakKeyDictionary将自动处理那些不再有其他引用的键对象的删除。

2.3.3 WeakSet

WeakSet是Python标准库中weakref模块提供的另一个类,它是一种特殊的集合,其中的元素是弱引用对象。这意味着,如果一个元素对象在程序中没有其他引用,那么它可以被垃圾回收并从集合中自动删除。

import weakref

# 创建一个WeakSet对象
ws = weakref.WeakSet()

# 定义一个类
class MyClass:
    def __init__(self, name):
        self.name = name

# 创建实例对象
obj1 = MyClass('Object 1')
obj2 = MyClass('Object 2')

# 将实例对象添加到WeakSet中
ws.add(obj1)
ws.add(obj2)

# 输出WeakSet的内容
print(ws)  # 输出: {<__main__.MyClass object at 0x...>, <__main__.MyClass object at 0x...>}

# 删除原始对象的引用
del obj1

# 等待垃圾回收

# 输出WeakSet的内容
print(ws)  # 输出: {<__main__.MyClass object at 0x...>}

在这个示例中,我们创建了一个WeakSet对象ws。然后,我们定义了一个MyClass类,并创建了两个实例对象obj1和obj2。我们将这两个对象添加到ws中。

当我们删除obj1的引用之后,obj1没有其他引用了,它可以被垃圾回收。由于ws中的元素是弱引用对象,当obj1被垃圾回收时,对应的元素也会从集合中自动删除。

因此,在等待垃圾回收之后,我们可以看到集合中只剩下obj2这个元素。这表明WeakSet将自动处理那些不再有其他引用的元素对象的删除。

2.3.4 finalize

finalize是Python标准库中weakref模块提供的一个函数,它可以用于注册在对象被垃圾回收时调用的回调函数。这个函数可以用来执行一些清理操作,例如关闭文件或释放资源。

以下是finalize函数的使用示例:

import weakref

# 定义一个类
class MyClass:
    def __init__(self, name):
        self.name = name

    def cleanup(self):
        print(f'Cleaning up {self.name}')

# 创建实例对象
obj1 = MyClass('Object 1')

# 注册回调函数
finalizer = weakref.finalize(obj1, obj1.cleanup)

# 输出对象是否还存活
print(finalizer.alive)  # 输出: True

# 删除原始对象的引用
del obj1

# 等待垃圾回收

# 输出对象是否还存活
print(finalizer.alive)  # 输出: False

# 回调函数被调用
# 输出: Cleaning up Object 1

在这个示例中,我们创建了一个MyClass类,并创建了一个实例对象obj1。我们通过weakref.finalize函数将obj1.cleanup方法注册为obj1对象的回调函数。

当我们删除obj1的引用之后,obj1没有其他引用了,它可以被垃圾回收。在对象被垃圾回收时,注册的回调函数obj1.cleanup会被调用。

因此,在等待垃圾回收之后,我们可以看到回调函数被调用,并输出"Cleaning up Object 1"。这表明finalize函数可以在对象被垃圾回收时执行所注册的回调函数。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值