python类与对象-如何在环状数据结构中管理内存
如何在环状数据结构中管理内存
问题举例
在python中,垃圾回收器通过引用计数来回收垃圾对象,
在某些环状数据结构(树,图...),存在对象间的循环引用,比如树的父节点引用子节点,
子节点同时引用父节点。测试同时del掉引用父子节点,两个对象不能被立即回收。
分析
当一个对象引用计数为0,或者只剩下弱引用时,这个对象会被释放。
类中有一个内置方法__del__,这个方法会在类对象释放时调用。
来个栗子
class A: def __del__(self): print("__del__") a1 = A() a2 = a1;
说明:
(1)A创建创建的对象有两个计数引用(a1和a2)
(2)当我们通过python -i test.py加载并运行这个文件到交互解释器中,__del__方法并没有被调用
(3)但是如果我们把a1, a2都指向别的对象时,A创建的对象引用计数为0,这时对象就会被释放
如下
class A: def __del__(self): print("__del__") a1 = A() a2 = a1; a1 = None a2 = None
注意:
这个时候可能有的朋友发现通过python test.py运行第一段代码时,__del__方法也会被调用,这是为什么呢?
其实答案很简单,通过python test.py运行代码后相当于直接退出程序,当一个程序退出时,这个程序内的所有变量都会被释放。
弱引用
弱引用不增加引用计数,使用弱引用访问对象得到对象引用
import weakref class A: def __del__(self): print("__del__") a1 = A() a2 = weakref.ref(a1) a3 = a2() print(a3 is a1) #True
解决思路
使用标准库weakref.ref,它可以创建一种能访问对象但是不增加引用计数的对象
栗子
有一个双向链表,有3个节点:1, 2, 3,变量head指向节点1,节点1右引用节点2,节点2右引用节点3,
节点3左弱引用节点2,节点2左弱引用节点1,如图
说明:当变量head指向None时,节点1对象被释放,节点1的右引用节点2被释放,节点2的右引用节点3被释放
代码
import weakref class Node: def __init__(self, data): self.data = data self._left = None self.right = None def add_right(self, node): self.right = node node._left = weakref.ref(self) @property def left(self): return self._left() def __str__(self): return 'Node:<%s>' % self.data def __del__(self): print('in __del__: delete %s' % self) def create_linklist(n): head = current = Node(1) for i in range(2, n + 1): node = Node(i) current.add_right(node) current = node return head head = create_linklist(1000) print(head.right, head.right.left) input() head = None import time for _ in range(1000): time.sleep(1) print('run...') input('wait...')
参考资料:python3实用编程技巧进阶