Python類方法__init__和__del__構造、析構過程分析

NO IMAGE

最近學習《Python參考手冊》學到Class部分,遇到了類的構造析構部分的問題:

1、什麼時候構造?
2、什麼時候析構?
3、成員變數如何處理?
4、Python中的共享成員函式如何訪問?
————————
探索過程:
1、經過查詢,Python中沒有專用的構造和解構函式,但是一般可以在__init__和__del__分別完成初始化和刪除操作,可用這個替代構造和析構。還有一個__new__用來定製類的建立過程,不過需要一定的配置,此處不做討論。
2、類的成員函式預設都相當於是public的,但是預設開頭為__的為私有變數,雖然是私有,但是我們還可以通過一定的手段訪問到,即Python不存在真正的私有變數。如:
複製程式碼 程式碼如下:
__priValue = 0 # 會自動變形為”_類名__priValue”的成員變數

3、由於Python的特殊性,全域性成員變數是共享的,所以類的例項不會為它專門分配內容空間,類似於static,具體使用參看下面的例子。

測試1:
複製程式碼 程式碼如下:
# encoding:utf8

class NewClass(object):
    num_count = 0 # 所有的例項都共享此變數,即不單獨為每個例項分配
    def __init__(self,name):
        self.name = name
        NewClass.num_count = 1
        print name,NewClass.num_count
    def __del__(self):
        NewClass.num_count -= 1
        print “Del”,self.name,NewClass.num_count
    def test():
        print “aa”

aa = NewClass(“Hello”)
bb = NewClass(“World”)
cc = NewClass(“aaaa”)

print “Over”

除錯執行:
複製程式碼 程式碼如下:
Hello 1
World 2
aaaa 3
Over
DeException l Hello 2
AttributeError: “‘NoneType’ object has no attribute ‘num_count'” in <bound method NewClass.__del__ of <__main__.NewClass object at 0x01AF18D0>> ignored
Exception AttributeError: “‘NoneType’ object has no attribute ‘num_count'” in <bound method NewClass.__del__ of <__main__.NewClass object at 0x01AF1970>> ignored

我們發現,num_count 是全域性的,當每建立一個例項,__init__()被呼叫,num_count 的值增一,當程式結束後,所有的例項會被析構,即呼叫__del__() 但是此時引發了異常。檢視異常為 “NoneType” 即 析構時NewClass 已經被垃圾回收,所以會產生這樣的異常。

但是,疑問來了?為什麼會這樣?按照C/C 等語言的經驗,不應該這樣啊!經過查詢資料,發現:

Python的垃圾回收過程與常用語言的不一樣,Python按照字典順序進行垃圾回收,而不是按照建立順序進行。所以當系統進行回收資源時,會按照類名A-Za-z的順序,依次進行,我們無法掌控這裡的流程。

明白這些,我們做如下嘗試:
複製程式碼 程式碼如下:
# encoding:utf8

class NewClass(object):
    num_count = 0 # 所有的例項都共享此變數,即不單獨為每個例項分配
    def __init__(self,name):
        self.name = name
        NewClass.num_count = 1
        print name,NewClass.num_count
    def __del__(self):
        NewClass.num_count -= 1
        print “Del”,self.name,NewClass.num_count
    def test():
        print “aa”

aa = NewClass(“Hello”)
bb = NewClass(“World”)
cc = NewClass(“aaaa”)

del aa
del bb
del cc

print “Over”

除錯輸出:
複製程式碼 程式碼如下:
Hello 1
World 2
aaaa 3
Del Hello 2
Del World 1
Del aaaa 0
Over

OK,一切按照我們預料的順序發生。
但是,我們總不能每次都手動回收吧?這麼做Python自己的垃圾回收還有什麼意義?

SO,繼續查詢,我們還可以通過self.__class__訪問到類本身,然後再訪問自身的共享成員變數,即 self.__class__.num_count , 將類中的NewClass.num_count替換為self.__class__.num_count 編譯執行,如下:

複製程式碼 程式碼如下:
# encoding:utf8

class NewClass(object):
    num_count = 0 # 所有的例項都共享此變數,即不單獨為每個例項分配
    def __init__(self,name):
        self.name = name
        self.__class__.num_count = 1
        print name,NewClass.num_count
    def __del__(self):
        self.__class__.num_count -= 1
        print “Del”,self.name,self.__class__.num_count
    def test():
        print “aa”

aa = NewClass(“Hello”)
bb = NewClass(“World”)
cc = NewClass(“aaaa”)

print “Over”

結果:
複製程式碼 程式碼如下:
Hello 1
World 2
aaaa 3
Over
Del Hello 2
Del World 1
Del aaaa 0

Perfect!我們完美地處理了這個問題!

PS:

書上又提到了一些問題,在這裡作補充(僅作為參考):

__new__()是唯一在例項建立之前執行的方法,一般用在定義元類時使用。

del xxx 不會主動呼叫__del__方法,只有引用計數==0時,__del__()才會被執行,並且定義了__del_()的例項無法被Python的迴圈垃圾收集器收集,所以儘量不要自定義__del__()。一般情況下,__del__() 不會破壞垃圾處理器。

實驗中發現垃圾回收自動呼叫了__del__, 這與書上所說又不符,不知是什麼原因,需要繼續學習。

您可能感興趣的文章:

詳細解讀Python中的__init__()方法python中的__init__ 、__new__、__call__小結詳解Python中的__init__和__new__Python中__init__.py檔案的作用詳解Python RuntimeError: thread.__init__() not called解決方法詳解Python中的__new__、__init__、__call__三個特殊方法Python中__init__和__new__的區別詳解Python中的__new__與__init__魔術方法理解筆記Python中__new__與__init__方法的區別詳解python中子類繼承父類的__init__方法例項