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

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

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

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

__new__方法的特性:

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

一般類的寫法和實(shí)例化如下:

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

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

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

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

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

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

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

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

而如果新式類中重寫了__new__()方法,那么你可以自由選擇任意一個(gè)的其他的新式類(必定要是新式類,只有新式類必定都有__new__(),因?yàn)樗行率筋惗际莖bject的后代,而經(jīng)典類則沒(méi)有__new__()方法)的__new__()方法來(lái)制造實(shí)例,包括這個(gè)新式類的所有前代類和后代類,只要它們不會(huì)造成遞歸死循環(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中沒(méi)有定義__new__()方法,那么會(huì)自動(dòng)調(diào)用其父類的__new__()方法來(lái)制造實(shí)例,即 Foo.__new__(cls, *args, **kwargs)。
# 在任何新式類的__new__()方法,不能調(diào)用自身的__new__()來(lái)制造實(shí)例,因?yàn)檫@會(huì)造成死循環(huán)。因此必須避免類似以下的寫法:
# 在Foo中避免:return Foo.__new__(cls, *args, **kwargs)或return cls.__new__(cls, *args, **kwargs)。Child同理。
# 使用object或者沒(méi)有血緣關(guān)系的新式類的__new__()是安全的,但是如果是在有繼承關(guān)系的兩個(gè)類之間,應(yīng)避免互調(diào)造成死循環(huán),例如:(Foo)return Child.__new__(cls), (Child)return Foo.__new__(cls)。
class Stranger(object):
    ...
# 在制造Stranger實(shí)例時(shí),會(huì)自動(dòng)調(diào)用 object.__new__(cls)

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

注意:如果__new__()沒(méi)有返回cls(即當(dāng)前類)的實(shí)例,那么當(dāng)前類的__init__()方法是不會(huì)被調(diào)用的。如果__new__()返回其他類(新式類或經(jīng)典類均可)的實(shí)例,那么只會(huì)調(diào)用被返回的那個(gè)類的構(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其實(shí)是Stranger類的實(shí)例。

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

單例模式

最后,讓我們使用__new__()方法來(lái)實(shí)現(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)建類實(shí)例的代碼也可以更簡(jiǎn)單的使用下面的代碼實(shí)現(xiàn)
            # cls._singleton = object.__new__(cls, *args, **kwargs)
        return cls._singleton

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

#輸出:(可以看到這兩個(gè)實(shí)例對(duì)象的地址是一樣的說(shuō)明是一個(gè)實(shí)例對(duì)象)
<__main__.OneOnly at 0x14c6e043dd8>
<__main__.OneOnly at 0x14c6e043dd8>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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