1. 程式人生 > >Python 之 __new__() 方法與實例化

Python 之 __new__() 方法與實例化

因此 range 打印 避免 細節設計 顯示 __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__()負責大樓的細節設計,建造,裝修使其可交付給客戶。

Python 之 __new__() 方法與實例化