0%

[python]弱引用weakref介绍

虽然一般情况下python无需像cpp一样需要手动 内存申请和释放,但是某些情况,我们需要自己去管理对象的销毁,让它的生命周期符合我们的预期。weakref就是一种方式。

1. 概念#

首先需要了解的是在 Python 里每个对象都有一个 引用计数,当这个引用计数为 0 时,Python 的garbage collection(GC)是可以 安全销毁这个对象的, 比如对一个对象创建引用则计数加 1,删除引用则计数减 1 。

weakref 模块允许 对一个对象创建弱引用,弱引用不像正常引用, 弱引用不会增加引用计数,也就是说 当一个对象上只有弱引用时,GC是可以销毁该对象的

2 weakref.ref#

2.1 weakref.ref用法#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
>>> import weakref
>>> import sys
>>> class DBO(object):
... pass

>>> dbo1 = DBO() # 创建对象,引用+1
>>> sys.getrefcount(dbo1) # 调用sys.getrefcount,该函数引用了dbo1,对象引用+1
2
>>> weakref_dbo = weakref.ref(dbo1) # 创建弱引用
>>> sys.getrefcount(dbo1) # 弱引用没有增加引用计数
2
>>> weakref_dbo # 弱引用指向的对象
<weakref at 0x7f9b0316d3c0; to 'DBO' at 0x7f9b03166ed0>
>>> dbo2 = weakref_dbo() # 获取弱引用指向的对象
>>> dbo1 is dbo2 # dbo1和dbo2引用的是同一个对象
True
>>> sys.getrefcount(dbo1) # 对象上的引用计数加 1
3
>>> sys.getrefcount(dbo2)
3
>>> dbo1 = None # 删除引用
>>> sys.getrefcount(dbo1) # 这个数字是None对象的引用次数
2545

>>> weakref_dbo
<weakref at 0x7f9b0316d3c0; to 'DBO' at 0x7f9b03166ed0>
>>> dbo2 = None # 删除引用
>>> weakref_dbo # 当对象引用计数为0时,弱引用失效
<weakref at 0x7f9b0316d3c0; dead>
>>> sys.getrefcount(dbo1)
2546
>>>

2.1 weakref.ref源码#

接下来我们看看它的源码套餐,定义在objects/weakrefObjects.c中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
PyObject *
PyWeakref_NewRef(PyObject *ob, PyObject *callback)
{
/*PyWeakReference是一个弱引用对象,result做临时存储*/
PyWeakReference *result = NULL;
PyWeakReference **list;
PyWeakReference *ref, *proxy;
/*检查当前对象类型是否支持弱引用*/
if (!PyType_SUPPORTS_WEAKREFS(Py_TYPE(ob))) {
PyErr_Format(PyExc_TypeError,
"cannot create weak reference to '%s' object",
Py_TYPE(ob)->tp_name);
return NULL;
}
/*获取弱引用链表指针*/
list = GET_WEAKREFS_LISTPTR(ob);
/*根据*list,给 ref 和 proxy 赋值*/
get_basic_refs(*list, &ref, &proxy);
if (callback == Py_None)
callback = NULL;
if (callback == NULL)
/* return existing weak reference if it exists */
result = ref;
if (result != NULL)
/*给当前weak reference引用加1*/
Py_INCREF(result);
else {
/* Note: new_weakref() can trigger cyclic GC, so the weakref
list on ob can be mutated. This means that the ref and
proxy pointers we got back earlier may have been collected,
so we need to compute these values again before we use
them.
使用之前,先确认ob对象没有被析构
*/
result = new_weakref(ob, callback);
if (result != NULL) {
get_basic_refs(*list, &ref, &proxy);
if (callback == NULL) {
if (ref == NULL)
insert_head(result, list);
else {
/* Someone else added a ref without a callback
during GC. Return that one instead of this one
to avoid violating the invariants of the list
of weakrefs for ob. */
Py_DECREF(result);
Py_INCREF(ref);
result = ref;
}
}
else {
PyWeakReference *prev;

prev = (proxy == NULL) ? ref : proxy;
if (prev == NULL)
insert_head(result, list);
else
insert_after(result, prev);
}
}
}
return (PyObject *) result;
}

  1. weakref.proxy proxy 像是弱引用对象,它们的行为就是它们所引用的对象的行为,这样就 不必 首先调用弱引用对象来访问背后的对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    >>> from socket import *
    >>> s = socket(AF_INET, SOCK_STREAM)
    >>> ref_s = weakref.ref(s)
    >>> ref_s
    <weakref at 0x7f9b0316d3c0; to '_socketobject' at 0x7f9b0310b910>
    >>> s
    <socket._socketobject object at 0x7f9b0310b910>
    >>> proxy_s = weakref.proxy(s)
    >>> proxy_s
    <weakproxy at 0x7f9b03117208 to _socketobject at 0x7f9b0310b910>
    >>>
    >>> ref_s.close() # 不能直接调用对象方法
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    AttributeError: 'weakref' object has no attribute 'close'
>>> ref_s().close()              #  不能直接调用对象方法,要加上()
>>>
>>> proxy_s.close()              #  可以直接调用对象方法
>>> 
>>> sys.getrefcount(s)
2
>>> ref_s
<weakref at 0x7f9b0316d3c0; to '_socketobject' at 0x7f9b0310b910>
>>> r = ref_s()
>>> r.close()
>>> sys.getrefcount(s)
3
>>> ref_s
<weakref at 0x7f9b0316d3c0; to '_socketobject' at 0x7f9b0310b910>
>>> del ref_s
>>> ref_s
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'ref_s' is not defined
>>>