Python __new__ 和 __init__ 的區(qū)別

__new__() 是在新式類中新出現(xiàn)的方法,它作用在構(gòu)造方法( __init__() )建造實(shí)例之前. 可以這么理解,在 Python 中存在于類里面的構(gòu)造方法 __init__() 負(fù)責(zé)將類進(jìn)行實(shí)例化,而在 __init__() 啟動之前,__new__() 決定是否要使用該 __init__() 方法,因?yàn)?__new__() 也可以調(diào)用其他類的構(gòu)造方法或者直接返回別的對象來作為本類的實(shí)例。

如果將類比喻為工廠,那么 __init__() 方法則是該工廠的生產(chǎn)工人,__init__() 方法接受的初始化參數(shù)則是生產(chǎn)所需原料,__init__() 方法會按照方法中的語句負(fù)責(zé)將原料加工成實(shí)例以供工廠出貨。而 __new__() 則是生產(chǎn)部經(jīng)理,__new__() 方法可以決定是否將原料提供給該生產(chǎn)部工人,同時它還決定著出貨產(chǎn)品是否為該生產(chǎn)部的產(chǎn)品,因?yàn)檫@名經(jīng)理可以借該工廠的名義向客戶出售完全不是該工廠的產(chǎn)品。

__new__() 方法的特性:

  • __new__() 方法是在類準(zhǔn)備將自身實(shí)例化時調(diào)用。
  • __new__() 方法始終都是類的靜態(tài)方法,即使沒有被加上靜態(tài)方法裝飾器。

類的實(shí)例化和構(gòu)造方法通常是這個樣子:

class MyClass(object):
    def __new__(cls, *args, **kwargs):
        return object.__new__(cls, *args, **kwargs)
    def __init__(self, *args, **kwargs):
        ...
            
# 實(shí)例化
myclass = MyClass(*args, **kwargs)

正如以上代碼所示,一個類可以有多個位置參數(shù)和多個命名參數(shù),而在實(shí)例化開始之后,在調(diào)用 __init__() 方法之前,Python 首先調(diào)用 __new__() 方法.

__new__ 的參數(shù)解釋:

  • cls 表示當(dāng)前類.
  • *args**kwargs 分別表示該類進(jìn)行初始化時, 輸入的位置參數(shù)和命名參數(shù)。
    __new__ 函數(shù)一般會有返回語句, 在返回語句中: object.__new__() 表示調(diào)用 object 類的 __new__() 函數(shù), 也可以使用 super().__new__(), 表示調(diào)用當(dāng)前類的父類的 __new__(), 如果父類沒有自定義 __new__(), 就會調(diào)用該父類的父類的 __new__(), 以此類推, 直到 object 類. return 中的 __new__() 函數(shù)的第一個參數(shù) cls 表示將要返回的類的類型, cls 表示用于返回當(dāng)前類, 也可以返回其他類.

事實(shí)上如果(新式)類中沒有重寫 __new__() 方法,即在定義新式類時沒有重新定義 __new__() 時,Python 默認(rèn)是調(diào)用該類的直接父類的 __new__() 方法來構(gòu)造該類的實(shí)例,如果該類的父類也沒有重寫 __new__(),那么將一直按此規(guī)則追溯至 object 類的 __new__() 方法,因?yàn)?object 類是所有新式類的基類。

如果新式類中重寫了 __new__() 方法,那么你可以自由選擇任意一個的其他的新式類(必定要是新式類,只有新式類必定都有 __new__(),因?yàn)樗行率筋惗际?object 的后代,而經(jīng)典類則沒有 __new__() 方法)的 __new__() 方法來制造實(shí)例,包括這個新式類的所有前代類和后代類,只要它們不會造成遞歸死循環(huán)。具體看以下代碼解釋:

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

class Child(Foo):
    def __new__(cls, *args, **kwargs):
        return super().__new__(cls, *args, **kwargs)
        
class Stranger(object):
    def __new__(cls, *args, **kwargs):
        ...
  • 如果Child中沒有定義 __new__() 方法,那么會自動調(diào)用其父類的 __new__() 方法來制造實(shí)例,即 Foo.__new__(cls, *args, **kwargs)
  • 在任何新式類的 __new__() 方法,不能調(diào)用自身的 __new__() 來制造實(shí)例,因?yàn)檫@會造成死循環(huán)。因此必須避免類似以下的寫法:
  1. Foo 中避免:return Foo.__new__(cls, *args, **kwargs)return cls.__new__(cls, *args, **kwargs)Child 同理。
  2. 使用 object 或者沒有血緣關(guān)系的新式類的 __new__() 是安全的,但是如果是在有繼承關(guān)系的兩個類之間,應(yīng)避免互調(diào)造成死循環(huán),例如: (Foo) return Child.__new__(cls) , (Child) return Foo.__new__(cls)
  • 在制造 Stranger 實(shí)例時,會自動調(diào)用 object.__new__(cls).

通常來說,新式類開始實(shí)例化時,__new__() 方法會返回 clscls 指代當(dāng)前類)的實(shí)例,然后該類的 __init__() 方法作為構(gòu)造方法會接收這個實(shí)例(即self)作為自己的第一個參數(shù),然后依次傳入 __new__() 方法中接收的位置參數(shù)和命名參數(shù)。

__new__() 除了返回 cls (當(dāng)前類) 的實(shí)例之外,還可以有其他用法:

  • 返回其他類:
class Foo(object):
    def __new__(cls, *args, **kwargs):
        return object.__new__(Stranger, *args, **kwargs) 
        
    def __init__(self, *args, **kwargs):
        ... 

class Stranger(object):
    ...

foo = Foo()
print(type(foo))    

打印的結(jié)果顯示 foo 其實(shí)是 Stranger 類的實(shí)例。

  • 返回其他數(shù)據(jù)類型:
a = 10

class Foo(object):
    def __new__(cls, *args, **kwargs):
        print("__new__ is called.")
        return a
    def __init__(self, *args, **kwargs):
        ...
Foo()

執(zhí)行結(jié)果為:

__new__ is called
10

可以這么描述 __new__()__ini__() 的區(qū)別,在新式類中 __new__() 才是真正的實(shí)例化方法,為類提供外殼制造出實(shí)例框架,然后調(diào)用該框架內(nèi)的構(gòu)造方法 __init__() 使其豐滿。

如果以建房子做比喻,__new__() 方法負(fù)責(zé)開發(fā)地皮,打下地基,并將原料存放在工地。而 __init__() 方法負(fù)責(zé)從工地取材料建造出地皮開發(fā)招標(biāo)書中規(guī)定的大樓,__init__() 負(fù)責(zé)大樓的細(xì)節(jié)設(shè)計,建造,裝修使其可交付給客戶。

注意:如果 __new__() 沒有返回 cls(即當(dāng)前類)的實(shí)例,那么當(dāng)前類的 __init__() 方法是不會被調(diào)用的。如果 __new__() 返回其他類(新式類或經(jīng)典類均可)的實(shí)例,那么只會調(diào)用被返回的那個類的構(gòu)造方法。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,775評論 0 9
  • 文/樂僧 很多人想跟我一樣學(xué)習(xí)寫作。但不知如何開始,這就需要老師指導(dǎo),至少需要看一些這方面的指導(dǎo)書。 上網(wǎng)搜索知乎...
    樂僧書田閱讀 372評論 0 7
  • 姓名:柴吉毅 慈溪市創(chuàng)強(qiáng)電氣有限公司 【日精進(jìn)打卡第13天】 【知~學(xué)習(xí)】 看了《活法》一篇章 《六項精進(jìn)大綱》1...
    大大大大寶_1ed2閱讀 211評論 0 0
  • 運(yùn)行環(huán)境 python3.6.7、tensorflow1.4.0 思路 這里用numpy對cos函數(shù)進(jìn)行間隔采樣,...
    YinliX閱讀 1,394評論 0 0