Skip to content

Commit de3da0d

Browse files
committed
8.23小节完成
1 parent bb8eef2 commit de3da0d

File tree

2 files changed

+224
-4
lines changed

2 files changed

+224
-4
lines changed

cookbook/c08/p23_weakref.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/env python
2+
# -*- encoding: utf-8 -*-
3+
"""
4+
Topic: 弱引用
5+
Desc :
6+
"""
7+
8+
import weakref
9+
10+
11+
class Node:
12+
def __init__(self, value):
13+
self.value = value
14+
self._parent = None
15+
self.children = []
16+
17+
def __repr__(self):
18+
return 'Node({!r:})'.format(self.value)
19+
20+
# property that manages the parent as a weak-reference
21+
@property
22+
def parent(self):
23+
return None if self._parent is None else self._parent()
24+
25+
@parent.setter
26+
def parent(self, node):
27+
self._parent = weakref.ref(node)
28+
29+
def add_child(self, child):
30+
self.children.append(child)
31+
child.parent = self
32+
33+
34+
# Class just to illustrate when deletion occurs
35+
class Data:
36+
def __del__(self):
37+
print('Data.__del__')
38+
39+
40+
# Node class involving a cycle
41+
class Node1:
42+
def __init__(self):
43+
self.data = Data()
44+
self.parent = None
45+
self.children = []
46+
47+
def add_child(self, child):
48+
self.children.append(child)
49+
child.parent = self
50+
51+
52+
a = Data()
53+
del a
54+
# print(a) NameError: name 'a' is not defined
55+
a = Node1()
56+
del a
57+
# print(a)
58+
a = Node1()
59+
a.add_child(Node1())
60+
print('--------last del start------------')
61+
del a # Not deleted (no message)
62+
print('--------last del end------------')
63+
# print(a)
64+
print('11111111111111111')
Lines changed: 160 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,174 @@
11
============================
2-
8.23 循环数据结构的内存管理
2+
8.23 循环引用数据结构的内存管理
33
============================
44

55
----------
66
问题
77
----------
8-
todo...
8+
你的程序创建了很多循环引用数据结构(比如树、图、观察者模式等),你碰到了内存管理难题。
9+
10+
|
911
1012
----------
1113
解决方案
1214
----------
13-
todo...
15+
一个简单的循环引用数据结构例子就是一个树形结构,双亲节点有指针指向孩子节点,孩子节点又返回来指向双亲节点。
16+
这种情况下,可以考虑使用 ``weakref`` 库中的弱引用。例如:
17+
18+
.. code-block:: python
19+
20+
import weakref
21+
22+
class Node:
23+
def __init__(self, value):
24+
self.value = value
25+
self._parent = None
26+
self.children = []
27+
28+
def __repr__(self):
29+
return 'Node({!r:})'.format(self.value)
30+
31+
# property that manages the parent as a weak-reference
32+
@property
33+
def parent(self):
34+
return None if self._parent is None else self._parent()
35+
36+
@parent.setter
37+
def parent(self, node):
38+
self._parent = weakref.ref(node)
39+
40+
def add_child(self, child):
41+
self.children.append(child)
42+
child.parent = self
43+
44+
这种是想方式允许parent静默终止。例如:
45+
46+
.. code-block:: python
47+
48+
>>> root = Node('parent')
49+
>>> c1 = Node('child')
50+
>>> root.add_child(c1)
51+
>>> print(c1.parent)
52+
Node('parent')
53+
>>> del root
54+
>>> print(c1.parent)
55+
None
56+
>>>
57+
58+
|
1459
1560
----------
1661
讨论
1762
----------
18-
todo...
63+
循环引用的数据结构在Python中是一个很棘手的问题,因为正常的垃圾回收机制不能适用于这种情形。
64+
例如考虑如下代码:
65+
66+
.. code-block:: python
67+
68+
# Class just to illustrate when deletion occurs
69+
class Data:
70+
def __del__(self):
71+
print('Data.__del__')
72+
73+
# Node class involving a cycle
74+
class Node:
75+
def __init__(self):
76+
self.data = Data()
77+
self.parent = None
78+
self.children = []
79+
80+
def add_child(self, child):
81+
self.children.append(child)
82+
child.parent = self
83+
84+
下面我们使用这个代码来做一些垃圾回收试验:
85+
86+
.. code-block:: python
87+
88+
>>> a = Data()
89+
>>> del a # Immediately deleted
90+
Data.__del__
91+
>>> a = Node()
92+
>>> del a # Immediately deleted
93+
Data.__del__
94+
>>> a = Node()
95+
>>> a.add_child(Node())
96+
>>> del a # Not deleted (no message)
97+
>>>
98+
99+
可以看到,最后一个的删除时打印语句没有出现。原因是Python的垃圾回收机制是基于简单的引用计数。
100+
当一个对象的引用数变成0的时候才会立即删除掉。而对于循环引用这个条件永远不会成立。
101+
因此,在上面例子中最后部分,父节点和孩子节点互相拥有对方的引用,导致每个对象的引用计数都不可能变成0。
102+
103+
Python有另外的垃圾回收器来专门针对循环引用的,但是你永远不知道它什么时候会触发。
104+
另外你还可以手动的触发它,但是代码看上去很挫:
105+
106+
.. code-block:: python
107+
108+
>>> import gc
109+
>>> gc.collect() # Force collection
110+
Data.__del__
111+
Data.__del__
112+
>>>
113+
114+
如果循环引用的对象自己还定义了自己的 ``__del__()`` 方法,那么会让情况变得更糟糕。
115+
假设你像下面这样给Node定义自己的 ``__del__()`` 方法:
116+
117+
.. code-block:: python
118+
119+
# Node class involving a cycle
120+
class Node:
121+
def __init__(self):
122+
self.data = Data()
123+
self.parent = None
124+
self.children = []
125+
126+
def add_child(self, child):
127+
self.children.append(child)
128+
child.parent = self
129+
130+
# NEVER DEFINE LIKE THIS.
131+
# Only here to illustrate pathological behavior
132+
def __del__(self):
133+
del self.data
134+
del.parent
135+
del.children
136+
137+
这种情况下,垃圾回收永远都不会去回收这个对象的,还会导致内存泄露。
138+
如果你试着去运行它会发现,``Data.__del__`` 消息永远不会出现了,甚至在你强制内存回收时:
139+
140+
.. code-block:: python
141+
142+
>>> a = Node()
143+
>>> a.add_child(Node()
144+
>>> del a # No message (not collected)
145+
>>> import gc
146+
>>> gc.collect() # No message (not collected)
147+
>>>
148+
149+
弱引用消除了引用循环的这个问题,本质来讲,弱引用就是一个对象指针,它不会增加它的引用计数。
150+
你可以通过 ``weakref`` 来创建弱引用。例如:
151+
152+
.. code-block:: python
153+
154+
>>> import weakref
155+
>>> a = Node()
156+
>>> a_ref = weakref.ref(a)
157+
>>> a_ref
158+
<weakref at 0x100581f70; to 'Node' at 0x1005c5410>
159+
>>>
160+
为了访问弱引用所引用的对象,你可以像函数一样去调用它即可。如果那个对象还存在就会返回它,否则就返回一个None
161+
由于原始对象的引用计数没有增加,那么就可以去删除它了。例如;
162+
163+
.. code-block:: python
164+
165+
>>> print(a_ref())
166+
<__main__.Node object at 0x1005c5410>
167+
>>> del a
168+
Data.__del__
169+
>>> print(a_ref())
170+
None
171+
>>>
172+
173+
通过这里演示的弱引用技术,你会发现不再有循环引用问题了,一旦某个节点不被使用了,垃圾回收器立即回收它。
174+
你还能参考8.25小节关于弱引用的另外一个例子。

0 commit comments

Comments
 (0)