摘要
本文討論了Python中__init__和__new__方法。
__new__和__init__具有不同的功能。并且對于Python的新類和舊類而言功能也不同。
__new__和__init__功能上的區(qū)別
__new__和__init__的主要區(qū)別在于:__new__是用來創(chuàng)造一個類的實例的(constructor),而__init__是用來初始化一個實例的(initializer)。
Python的新類和舊類
Python中的類分為新類和舊類。舊類是Python3之前的類,舊類并不是默認(rèn)繼承object類,而是繼承type類。
Python2中的舊類如下面代碼所示:
class oldStyleClass: # inherits from 'type'
pass
Python2中定義一個新類:
class newStyleClass(object): # explicitly inherits from 'object'
pass
在Python3中所有的類均默認(rèn)繼承object,所以并不需要顯式地指定object為基類。
以object為基類可以使得所定義的類具有新類所對應(yīng)的方法(methods)和屬性(properties)。
在下面的文章中我們會分別基于新類和舊類探討__new__和__init__。
__new__和__init__參數(shù)的不同
__new__所接收的第一個參數(shù)是cls,而__init__所接收的第一個參數(shù)是self。這是因為當(dāng)我們調(diào)用__new__的時候,該類的實例還并不存在(也就是self所引用的對象還不存在),所以需要接收一個類作為參數(shù),從而產(chǎn)生一個實例。而當(dāng)我們調(diào)用__init__的時候,實例已經(jīng)存在,因此__init__接受self作為第一個參數(shù)并對該實例進行必要的初始化操作。這也意味著__init__是在__new__之后被調(diào)用的。
Python舊類中的__new__和__init__
Python的舊類中實際上并沒有__new__方法。因為舊類中的__init__實際上起構(gòu)造器的作用。所以如果我們定義如下舊類:
class oldStyleClass:
def __new__(cls):
print("__new__ is called") # this line will never get called during construction
oldStyleClass()
程序輸出結(jié)果如下:
<__main__.oldStyleClass instance at 0x109c45518>
可見創(chuàng)建及初始化對象的過程并沒有調(diào)用__new__。實際上,除非顯式調(diào)用:oldStyleClass.__new__(oldStyleClass),該類中的__new__方法中的內(nèi)容永遠(yuǎn)不會被調(diào)用。因為舊類構(gòu)造實例并不會調(diào)用__new__方法。
但如果我們重載__init__方法:
class oldStyleClass:
def __init__(self):
print("__init__ is called")
oldStyleClass()
該程序?qū)敵?/p>
__init__ is called
<__main__.oldStyleClass instance at 0x1091992d8>
如果我們在__init__中加上return語句,將會導(dǎo)致TypeError: __init__() should return None的錯誤。
class oldStyleClass:
def __init__(self):
return 29
oldStyleClass()
程序結(jié)果如下:
TypeError: __init__() should return None
這意味著對于Python的舊類而言,我們無法控制__init__函數(shù)的返回值。
Python新類中的__new__和__init__
Python的新類允許用戶重載__new__和__init__方法,且這兩個方法具有不同的作用。__new__作為構(gòu)造器,起創(chuàng)建一個類實例的作用。而__init__作為初始化器,起初始化一個已被創(chuàng)建的實例的作用。
如下面代碼是所示:
class newStyleClass(object):
# In Python2, we need to specify the object as the base.
# In Python3 it's default.
def __new__(cls):
print("__new__ is called")
return super(newStyleClass, cls).__new__(cls)
def __init__(self):
print("__init__ is called")
print("self is: ", self)
newStyleClass()
結(jié)果如下:
__new__ is called
__init__ is called
self is: <__main__.newStyleClass at 0x109290890>
<__main__.newStyleClass at 0x109290890>
創(chuàng)建類實例并初始化的過程中__new__和__init__被調(diào)用的順序也能從上面代碼的輸出結(jié)果中看出:__new__函數(shù)首先被調(diào)用,構(gòu)造了一個newStyleClass的實例,接著__init__函數(shù)在__new__函數(shù)返回一個實例的時候被調(diào)用,并且這個實例作為self參數(shù)被傳入了__init__函數(shù)。
這里需要注意的是,如果__new__函數(shù)返回一個已經(jīng)存在的實例(不論是哪個類的),__init__不會被調(diào)用。如下面代碼所示:
obj = 12
# obj can be an object from any class, even object.__new__(object)
class returnExistedObj(object):
def __new__(cls):
print("__new__ is called")
return obj
def __init(self):
print("__init__ is called")
returnExistedObj()
執(zhí)行結(jié)果如下:
__new__ is called
12
同時另一個需要注意的點是:
如果我們在__new__函數(shù)中不返回任何對象,則__init__函數(shù)也不會被調(diào)用。
如下面代碼所示:
class notReturnObj(object):
def __new__(cls):
print("__new__ is called")
def __init__(self):
print("__init__ is called")
print(notReturnObj())
執(zhí)行結(jié)果如下:
__new__ is called
None
可見如果__new__函數(shù)不返回對象的話,不會有任何對象被創(chuàng)建,__init__函數(shù)也不會被調(diào)用來初始化對象。
總結(jié)幾個點
__init__不能有返回值__new__函數(shù)直接上可以返回別的類的實例。如上面例子中的returnExistedObj類的__new__函數(shù)返回了一個int值。只有在
__new__返回一個新創(chuàng)建屬于該類的實例時當(dāng)前類的__init__才會被調(diào)用。如下面例子所示:
class sample(object):
def __str__(self):
print("sample")
class example(object):
def __new__(cls):
print("__new__ is called")
return sample()
def __init__(self):
print("__init__ is called")
example()
輸出結(jié)果為:
__new__ is called
sample