python類中__init__和__new__的區(qū)別

轉(zhuǎn)自 Python 之 new() 方法與實例化

__new__() 是在新式類中新出現(xiàn)的方法垂攘,它作用在構(gòu)造方法建造實例之前人弓,可以這么理解,在 Python 中存在于類里面的構(gòu)造方法 __init__() 負責(zé)將類的實例化,而在 __init__() 啟動之前待锈,__new__() 決定是否要使用該__init__() 方法湖员,因為__new__() 可以調(diào)用其他類的構(gòu)造方法或者直接返回別的對象來作為本類的實例贫悄。

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

__new__方法的特性:

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

一般類的寫法和實例化如下:

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

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

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

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

第一個參數(shù)cls是當(dāng)前正在實例化的類脑慧。如果要得到當(dāng)前類的實例魄眉,應(yīng)當(dāng)在當(dāng)前類中的 __new__() 方法語句中調(diào)用當(dāng)前類的父類的 __new__() 方法。

例如闷袒,如果當(dāng)前類是直接繼承自 object坑律,那當(dāng)前類的 __new__() 方法返回的對象應(yīng)該為:

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

注意:
事實上如果(新式)類中沒有重寫__new__()方法,即在定義新式類時沒有重新定義__new__()時囊骤,Python默認是調(diào)用該類的直接父類的__new__()方法來構(gòu)造該類的實例晃择,如果該類的父類也沒有重寫__new__()冀值,那么將一直按此規(guī)矩追溯至object的__new__()方法,因為object是所有新式類的基類宫屠。

而如果新式類中重寫了__new__()方法池摧,那么你可以自由選擇任意一個的其他的新式類(必定要是新式類,只有新式類必定都有__new__()激况,因為所有新式類都是object的后代作彤,而經(jīng)典類則沒有__new__()方法)的__new__()方法來制造實例,包括這個新式類的所有前代類和后代類乌逐,只要它們不會造成遞歸死循環(huán)竭讳。具體看以下代碼解釋:

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__()方法,那么會自動調(diào)用其父類的__new__()方法來制造實例,即 Foo.__new__(cls, *args, **kwargs)。
# 在任何新式類的__new__()方法腻贰,不能調(diào)用自身的__new__()來制造實例,因為這會造成死循環(huán)胰舆。因此必須避免類似以下的寫法:
# 在Foo中避免:return Foo.__new__(cls, *args, **kwargs)或return cls.__new__(cls, *args, **kwargs)。Child同理蹬挤。
# 使用object或者沒有血緣關(guān)系的新式類的__new__()是安全的缚窿,但是如果是在有繼承關(guān)系的兩個類之間,應(yīng)避免互調(diào)造成死循環(huán)焰扳,例如:(Foo)return Child.__new__(cls), (Child)return Foo.__new__(cls)倦零。
class Stranger(object):
    ...
# 在制造Stranger實例時,會自動調(diào)用 object.__new__(cls)

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

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

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)    

# 打印的結(jié)果顯示foo其實是Stranger類的實例钙态。

# 因此可以這么描述__new__()和__ini__()的區(qū)別慧起,在新式類中__new__()才是真正的實例化方法,
# 為類提供外殼制造出實例框架册倒,然后調(diào)用該框架內(nèi)的構(gòu)造方法__init__()使其豐滿。
# 如果以建房子做比喻磺送,__new__()方法負責(zé)開發(fā)地皮驻子,打下地基灿意,并將原料存放在工地。
# 而__init__()方法負責(zé)從工地取材料建造出地皮開發(fā)招標書中規(guī)定的大樓崇呵,__init__()負責(zé)大樓的細節(jié)設(shè)計缤剧,建造,裝修使其可交付給客戶域慷。

單例模式

最后荒辕,讓我們使用__new__()方法來實現(xiàn)Python中的單例模式。

class OneOnly(object):
    _singleton = None

    def __new__(cls, *args, **kwargs):
        if not cls._singleton:
            cls._singleton = super(OneOnly, cls).__new__(cls, *args, **kwargs)
            # 上面構(gòu)建類實例的代碼也可以更簡單的使用下面的代碼實現(xiàn)
            # cls._singleton = object.__new__(cls, *args, **kwargs)
        return cls._singleton

#實例化類
a1 = OneOnly()
a2 = OneOnly()
a1
a2

#輸出:(可以看到這兩個實例對象的地址是一樣的說明是一個實例對象)
<__main__.OneOnly at 0x14c6e043dd8>
<__main__.OneOnly at 0x14c6e043dd8>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末犹褒,一起剝皮案震驚了整個濱河市抵窒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叠骑,老刑警劉巖李皇,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異宙枷,居然都是意外死亡掉房,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門慰丛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卓囚,“玉大人,你說我怎么就攤上這事诅病『丛溃” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵睬隶,是天一觀的道長锣夹。 經(jīng)常有香客問我,道長苏潜,這世上最難降的妖魔是什么银萍? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮恤左,結(jié)果婚禮上贴唇,老公的妹妹穿的比我還像新娘。我一直安慰自己飞袋,他們只是感情好戳气,可當(dāng)我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著巧鸭,像睡著了一般瓶您。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天呀袱,我揣著相機與錄音贸毕,去河邊找鬼。 笑死夜赵,一個胖子當(dāng)著我的面吹牛明棍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播寇僧,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼摊腋,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了嘁傀?” 一聲冷哼從身側(cè)響起兴蒸,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎心包,沒想到半個月后类咧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡蟹腾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年痕惋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片娃殖。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡值戳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出炉爆,到底是詐尸還是另有隱情堕虹,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布芬首,位于F島的核電站赴捞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏郁稍。R本人自食惡果不足惜赦政,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望耀怜。 院中可真熱鬧恢着,春花似錦、人聲如沸财破。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽左痢。三九已至靡羡,卻和暖如春系洛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背亿眠。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工碎罚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留磅废,地道東北人纳像。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像拯勉,于是被迫代替她去往敵國和親竟趾。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,629評論 2 354

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