建構、初始與消滅


在之前的文件中所提到的,__init__()方法是定義物件建立後初始化的流程,也就是執行到__init__()方法時,物件實際上已建構完成,傳入__init__()的引數,並不是作為建構物件之用,而是作為初始物件之用。

實際上要決定如何建構物件,必須定義__new__()方法,這個方法的第一個參數總是傳入類別本身,之後可接任意參數作為建構物件之用。

__new__() 方法可以傳回物件,如果傳回的物件是第一個參數的類別實例,則會執行__init__()方法(如果有定義的話),而__init__()方法的第一個參 數綁定所傳回的物件。如果沒有傳回第一個參數的類別實例(傳回別的實例或None),則不會執行__init__()方法(即使有定義)。

一個簡單測試建構與初始流程的例子如下所示:
>>> class Some:
...     def __new__(clz, isClzInstance):
...         print('__new__')
...         if isClzInstance:
...             return object.__new__(clz)
...         else:
...             return None
...     def __init__(self, isClzInstance):
...         print('__init__')
...         print(isClzInstance)
...
>>> Some(True)
__new__
__init__
True
>>> Some(False)
__new__
>>>


由於若傳回__new__()第一個參數的類別實例,就會執行__init__()方法,所以__new__()與__init__()通常會具有相同個數的參數。藉由定義__new__()方法,可以決定如何建構物件與初始物件,一個應用的例子,就是實作 Singleton 模式。例如:
class Singleton:
__single = None
def __new__(clz):
if not Singleton.__single:
Singleton.__single = object.__new__(clz)
return Singleton.__single

def doSomething(self):
print("do something...XD")

singleton1 = Singleton()
singleton1.doSomething() # do something...XD
singleton2 = Singleton()
singleton2.doSomething() # do something...XD
print(singleton1 is singleton2) # True

在上面的程式中,一旦Singleton.__single有參考至物件,則直接傳回,從客戶端的角度則如常執行Singleton()來建構物件,但實際上傳回的都會是同一個物件(由於傳回的是Singleton實例,所以若有定義__init__()則會被執行,就單例物件來說,可於__init__()中進行狀態的回復)。

如果要定義物件被垃圾收集(Garbage collection)時,所要進行的資源清除動作,則可以定義__del__()方法,物件會被資源回收的資格,基本上就是參考至物件的變數計數為0的時候。例如:
>>> class Some:
...     def __del__(self):
...         print('__del__')
...
>>> s = Some()
>>> s = None
__del__
>>> s1 = Some()
>>> s2 = s1
>>> del s1
>>> del s2
__del__
>>>

在上例中,將s參考至None時,原先所參考的物件其變數計數就為0,所以在回收物件前執行了__del__(),而使用del刪除s1時,s2仍參考至物件,直到s2也被del刪除,物件符合回收資格,回收物件前執行了__del__()。