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实用编程技巧进阶

原文地址:https://www.cnblogs.com/marton/p/10847809.html