最近學習《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 > ignored
Exception AttributeError: "'NoneType' object has no attribute 'num_count'" in > 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__, 這與書上所說又不符,不知是什麼原因,需要繼續學習。