這個方法的作用是將一個對象從內存中清除之前,可以有機會做一些清理工作。
這裏之所以用 “有機會” 來形容是因爲這個方法是不穩定的:它並不總是在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)))