Python 之 __new__() 方法與實例化

new() 是在新式類中新出現的方法,它作用在構造方法建造實例之前,可以這麼理解,在 Python 中存在於類裏面的構造方法 init() 負責將類的實例化,而在 init() 啓動之前,new() 決定是否要使用該 init() 方法,因爲new() 可以調用其他類的構造方法或者直接返回別的對象來作爲本類的實例。

如果將類比喻爲工廠,那麼init()方法則是該工廠的生產工人,init()方法接受的初始化參數則是生產所需原料,init()方法會按照方法中的語句負責將原料加工成實例以供工廠出貨。而new()則是生產部經理,new()方法可以決定是否將原料提供給該生產部工人,同時它還決定着出貨產品是否爲該生產部的產品,因爲這名經理可以借該工廠的名義向客戶出售完全不是該工廠的產品。

new() 方法的特性:

new() 方法是在類準備將自身實例化時調用。
new() 方法始終都是類的靜態方法,即使沒有被加上靜態方法裝飾器。
類的實例化和它的構造方法通常都是這個樣子:

class MyClass(object):
    def __init__(self, *args, **kwargs):
        ...

# 實例化
myclass = MyClass(*args, **kwargs)

正如以上所示,一個類可以有多個位置參數和多個命名參數,而在實例化開始之後,在調用 init() 方法之前,Python 首先調用 new() 方法:

def __new__(cls, *args, **kwargs):
    ...

第一個參數cls是當前正在實例化的類。

如果要得到當前類的實例,應當在當前類中的 new() 方法語句中調用當前類的父類的 new() 方法。
例如,如果當前類是直接繼承自 object,那當前類的 new() 方法返回的對象應該爲:

def __new__(cls, *args, **kwargs):
    ...
    return object.__new__(cls)

注意:

  事實上如果(新式)類中沒有重寫new()方法,即在定義新式類時沒有重新定義new()時,Python默認是調用該類的直接父類的new()方法來構造該類的實例,如果該類的父類也沒有重寫new(),那麼將一直按此規矩追溯至object的new()方法,因爲object是所有新式類的基類。

  而如果新式類中重寫了new()方法,那麼你可以自由選擇任意一個的其他的新式類(必定要是新式類,只有新式類必定都有new(),因爲所有新式類都是object的後代,而經典類則沒有new()方法)的new()方法來製造實例,包括這個新式類的所有前代類和後代類,只要它們不會造成遞歸死循環。具體看以下代碼解釋:

class Foo(object):
    def __init__(self, *args, **kwargs):
        ...
    def __new__(cls, *args, **kwargs):
        return object.__new__(cls, *args, **kwargs)    

# 以上return等同於 
# return object.__new__(Foo, *args, **kwargs)
# return Stranger.__new__(cls, *args, **kwargs)
# return Child.__new__(cls, *args, **kwargs)

class Child(Foo):
    def __new__(cls, *args, **kwargs):
        return object.__new__(cls, *args, **kwargs)
# 如果Child中沒有定義__new__()方法,那麼會自動調用其父類的__new__()方法來製造實例,即 Foo.__new__(cls, *args, **kwargs)。
# 在任何新式類的__new__()方法,不能調用自身的__new__()來製造實例,因爲這會造成死循環。因此必須避免類似以下的寫法:
# 在Foo中避免:return Foo.__new__(cls, *args, **kwargs)或return cls.__new__(cls, *args, **kwargs)。Child同理。
# 使用object或者沒有血緣關係的新式類的__new__()是安全的,但是如果是在有繼承關係的兩個類之間,應避免互調造成死循環,例如:(Foo)return Child.__new__(cls), (Child)return Foo.__new__(cls)。
class Stranger(object):
    ...
# 在製造Stranger實例時,會自動調用 object.__new__(cls)

通常來說,新式類開始實例化時,new()方法會返回cls(cls指代當前類)的實例,然後該類的init()方法作爲構造方法會接收這個實例(即self)作爲自己的第一個參數,然後依次傳入new()方法中接收的位置參數和命名參數。

注意:如果new()沒有返回cls(即當前類)的實例,那麼當前類的init()方法是不會被調用的。如果new()返回其他類(新式類或經典類均可)的實例,那麼只會調用被返回的那個類的構造方法。

class Foo(object):
    def __init__(self, *args, **kwargs):
        ...
    def __new__(cls, *args, **kwargs):
        return object.__new__(Stranger, *args, **kwargs)  

class Stranger(object):
    ...

foo = Foo()
print type(foo)    

# 打印的結果顯示foo其實是Stranger類的實例。

# 因此可以這麼描述__new__()和__ini__()的區別,在新式類中__new__()纔是真正的實例化方法,爲類提供外殼製造出實例框架,然後調用該框架內的構造方法__init__()使其豐滿。
# 如果以建房子做比喻,__new__()方法負責開發地皮,打下地基,並將原料存放在工地。而__init__()方法負責從工地取材料建造出地皮開發招標書中規定的大樓,__init__()負責大樓的細節設計,建造,裝修使其可交付給客戶。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章