Python __new__ 和 __init__ 的區別
__new__()
是在新式類中新出現的方法,它作用在構造方法( __init__()
)建造例項之前. 可以這麼理解,在 Python 中存在於類裡面的構造方法 __init__()
負責將類進行例項化,而在 __init__()
啟動之前,__new__()
決定是否要使用該 __init__()
方法,因為 __new__()
也可以呼叫其他類的構造方法或者直接返回別的物件來作為本類的例項。
如果將類比喻為工廠,那麼 __init__()
方法則是該工廠的生產工人,__init__()
方法接受的初始化引數則是生產所需原料,__init__()
方法會按照方法中的語句負責將原料加工成例項以供工廠出貨。而 __new__()
則是生產部經理,__new__()
方法可以決定是否將原料提供給該生產部工人,同時它還決定著出貨產品是否為該生產部的產品,因為這名經理可以借該工廠的名義向客戶出售完全不是該工廠的產品。
__new__()
方法的特性:
__new__()
方法是在類準備將自身例項化時呼叫。__new__()
方法始終都是類的靜態方法,即使沒有被加上靜態方法裝飾器。
類的例項化和構造方法通常是這個樣子:
class MyClass(object):
def __new__(cls, *args, **kwargs):
return object.__new__(cls, *args, **kwargs)
def __init__(self, *args, **kwargs):
...
# 例項化
myclass = MyClass(*args, **kwargs)
正如以上程式碼所示,一個類可以有多個位置引數和多個命名引數,而在例項化開始之後,在呼叫 __init__()
方法之前,Python 首先呼叫 __new__()
方法.
__new__
的引數解釋:
cls
表示當前類.*args
和**kwargs
分別表示該類進行初始化時, 輸入的位置引數和命名引數。
__new__
函式一般會有返回語句, 在返回語句中:object.__new__()
表示呼叫object
類的__new__()
函式, 也可以使用super().__new__()
, 表示呼叫當前類的父類的__new__()
, 如果父類沒有自定義__new__()
, 就會呼叫該父類的父類的__new__()
, 以此類推, 直到object
類.return
中的__new__()
函式的第一個引數cls
表示將要返回的類的型別,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)
class Child(Foo):
def __new__(cls, *args, **kwargs):
return super().__new__(cls, *args, **kwargs)
class Stranger(object):
def __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)
。
- 在製造
Stranger
例項時,會自動呼叫object.__new__(cls)
.
通常來說,新式類開始例項化時,__new__()
方法會返回 cls
( cls
指代當前類)的例項,然後該類的 __init__()
方法作為構造方法會接收這個例項(即self
)作為自己的第一個引數,然後依次傳入 __new__()
方法中接收的位置引數和命名引數。
__new__()
除了返回 cls
(當前類) 的例項之外,還可以有其他用法:
- 返回其他類:
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))
列印的結果顯示 foo
其實是 Stranger
類的例項。
- 返回其他資料型別:
a = 10
class Foo(object):
def __new__(cls, *args, **kwargs):
print("__new__ is called.")
return a
def __init__(self, *args, **kwargs):
...
Foo()
執行結果為:
__new__ is called
10
可以這麼描述 __new__()
和 __ini__()
的區別,在新式類中 __new__()
才是真正的例項化方法,為類提供外殼製造出例項框架,然後呼叫該框架內的構造方法 __init__()
使其豐滿。
如果以建房子做比喻,__new__()
方法負責開發地皮,打下地基,並將原料存放在工地。而 __init__()
方法負責從工地取材料建造出地皮開發招標書中規定的大樓,__init__()
負責大樓的細節設計,建造,裝修使其可交付給客戶。
注意:如果 __new__()
沒有返回 cls
(即當前類)的例項,那麼當前類的 __init__()
方法是不會被呼叫的。如果 __new__()
返回其他類(新式類或經典類均可)的例項,那麼只會呼叫被返回的那個類的構造方法。
相關文章
- __new__和__init__的區別
- Python面試之理解__new__和__init__的區別Python面試
- Python中__new__和__init__的區別與聯絡Python
- Python語言中__init__與__new__的區別是什麼?Python
- python中__init__ 和__new__的對比Python
- 詳解Python中的__init__和__new__Python
- 一問搞懂python的__init__和__new__方法Python
- python類中super()和__init__()的區別Python
- python中的__init__ 、__new__、__call__小結Python
- python2中的__new__與__init__,新式類和經典類Python
- 簡述Python類中的 __init__、__new__、__call__ 方法Python
- Python中的__new__、__init__、__call__三個特殊方法Python
- Python中__init__的用法和理解Python
- Python__new__和__init__Python
- python的__init__()Python
- Python 中 is 和 == 的區別Python
- Python中is和==的區別Python
- python ruturn 和 print 的區別Python
- python中__new__的使用注意Python
- Python中的__new__()詳解Python
- Python之“==”和“is”區別Python
- Python中__init__的理解Python
- python和r語言的區別PythonR語言
- django classonlymethod 和 python classmethod的區別DjangoPythonSSM
- python is和==的區別是什麼?Python
- PHP, Python和Java的區別分享PHPPythonJava
- 認清Python下==和is的區別Python
- Python中range和xrange的區別Python
- Python中%r和%s的區別Python
- Python fopen,open,和popen的區別Python
- Python教程:return和yield的區別Python
- Python3中_和__的用途和區別Python
- Python中的@staticmethod和@classmethod的區別PythonSSM
- python的type和isinstance的區別Python
- Python和access的區別有哪些?Python教程Python
- Python2和Python3的區別Python
- python中的“__init__”函式Python函式
- Python引用型別和值型別的區別與使用Python型別