python中的__del__方法,內存回收和弱引用

這個方法的作用是將一個對象從內存中清除之前,可以有機會做一些清理工作。

這裏之所以用 “有機會” 來形容是因爲這個方法是不穩定的:它並不總是在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)))
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章