python内存回收和弱引用

这个方法的作用是将一个对象从内存中清除之前,可以有机会做一些清理工作。

这里之所以用 “有机会” 来形容是因为这个方法是不稳定的:它并不总是在del语句删除对象时被调用,当一个对象的命名空间被删除时,它也不一定被调用。

所以同样的需求最好使用上下文管理器来实现。

python(Cpython)中对象会包含一个引用计数。__del__方法只有当引用技术为0时才会执行。

class f:
    def __del__(self):
        print(id(self))
>> a = [f(),f()]
>> b = a
>> del a # 还有b在引用,所以没有执行__del__
>> del b
2414194009984
2414194010824

循环引用

当存在循环引用时,执行del函数,对应的__del__方法将不会执行。

class Parent:
    def __init__(self, *children):
        self.children = children
        for child in self.children:
            child.parent = self

    def __del__(self):
        print('Remove {}  id {}'.format(self.__class__, id(self)))

class Child:
    def __del__(self):
        print('Remove {}  id {}'.format(self.__class__,id(self)))
        
>> p = Parent(Child(),Child())
>> del p
# 并没有执行__del__方法的输出

这时可以使用垃圾回收接口–GC,来回收和显示这些不能删除的对象。

>> import gc 
>> gc.collect()
Remove <class '__main__.Child'>  id 2408580851920
Remove <class '__main__.Child'>  id 2408580852144
Remove <class '__main__.Parent'>  id 2408580852872

弱引用

默认对象间的引用为强引用,或者叫直接引用。上面的循环引用就是使用的强引用。这会影响引用计数。

如果我们需要循环引用,又想把清理代码写在__del__中,可以使用弱引用。

弱引用和强引用的寻找过程也是不同的:

# 弱引用
x.parent() #在函数中存在寻找父对象的逻辑
# 强引用
x.parent #直接指向的就是父对象

weakref模块定义了一系列使用弱引用而没有使用强引用的集合,它可以让我们创建一种特殊的字典,当这种字典的对象没有用时,可以保证被垃圾回收。

上面的类可以更改为这样的:

import weakref

class Parent:
    def __init__(self, *children):
        self.children = children
        for child in self.children:
            child.parent = weakref.ref(self) #使用weakref创建弱引用

    def __del__(self):
        print('Remove {}  id {}'.format(self.__class__, id(self)))

class Child:
    # 这里添加了一个使用 parent属性的函数,来演示区别
    def print_parent(self):
        parent = self.parent()
        if parent is not None:
            print(parent)
        else:
            # 为None的情况就是parent对象被内存回收机制处理了
            print('the parent instance was garbage collected')

    def __del__(self):
        print('Remove {}  id {}'.format(self.__class__,id(self)))