公眾號新增加了一個欄目,就是每天給大家解答一道Python常見的面試題,反正每天不貪多,一天一題,正好合適,只希望這個面試欄目,給那些正在準備面試的同學,提供一點點幫助!
小猿會從最基礎的面試題開始,每天一題。如果參考答案不夠好,或者有錯誤的話,麻煩大家可以在留言區給出自己的意見和討論,大家是要一起學習的 。
廢話不多說,開始今天的題目:
問:說說Python中的__new__和__init__的區別?
答:在Python中__new__和__init__具有不同的功能。並且對於Python的新類和舊類而言功能也不同。
__new__是在實例創建之前被調用的,因為它的任務就是創建實例然後返回該實例對象,是個靜態方法。
__init__是當實例對象創建完成後被調用的,然後設置對象屬性的一些初始值,通常用在初始化一個類實例的時候。是一個實例方法。
主要區別在於:__new__是用來創造一個類的實例的,而__init__是用來初始化一個實例的。
下文來源於:
https://www.jianshu.com/p/14b8ebf93b73
Python的新類和舊類Python中的類分為新類和舊類。舊類是Python3之前的類,舊類並不是默認繼承object類,而是繼承type類。
Python2中的舊類如下面代碼所示:
class oldStyleClass:
pass
Python2中定義一個新類:
class newStyleClass(object):
pass
在Python3中所有的類均默認繼承object,所以並不需要顯式地指定object為基類。
以object為基類可以使得所定義的類具有新類所對應的方法(methods)和屬性(properties)。
在下面的文章中我們會分別基於新類和舊類探討__new__和__init__。
__new__和__init__參數的不同__new__所接收的第一個參數是cls,而__init__所接收的第一個參數是self。這是因為當我們調用__new__的時候,該類的實例還並不存在(也就是self所引用的對象還不存在),所以需要接收一個類作為參數,從而產生一個實例。而當我們調用__init__的時候,實例已經存在,因此__init__接受self作為第一個參數並對該實例進行必要的初始化操作。這也意味著__init__是在__new__之後被調用的。
Python舊類中的__new__和__init__Python的舊類中實際上並沒有__new__方法。因為舊類中的__init__實際上起構造器的作用。所以如果我們定義如下舊類:
class oldStyleClass:
def __new__(cls):
print("__new__ is called")
oldStyleClass()
程序輸出結果如下:
<__main__.oldStyleClass instance at 0x109c45518>
可見創建及初始化對象的過程並沒有調用__new__。實際上,除非顯式調用:oldStyleClass.__new__(oldStyleClass),該類中的__new__方法中的內容永遠不會被調用。因為舊類構造實例並不會調用__new__方法。
但如果我們重載__init__方法:
class oldStyleClass:
def __init__(self):
print("__init__ is called")
oldStyleClass()
該程序將會輸出
__init__ is called
<__main__.oldStyleClass instance at 0x1091992d8>
如果我們在__init__中加上return語句,將會導致TypeError: __init__() should return None的錯誤。
class oldStyleClass:
def __init__(self):
return 29
oldStyleClass()
程序結果如下:
TypeError: __init__() should return None
這意味著對於Python的舊類而言,我們無法控制__init__函數的返回值。
Python新類中的__new__和__init__Python的新類允許用戶重載__new__和__init__方法,且這兩個方法具有不同的作用。__new__作為構造器,起創建一個類實例的作用。而__init__作為初始化器,起初始化一個已被創建的實例的作用。
如下面代碼是所示:
class newStyleClass(object):
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()
結果如下:
__new__ is called
__init__ is called
self is: <__main__.newStyleClass at 0x109290890>
<__main__.newStyleClass at 0x109290890>
創建類實例並初始化的過程中__new__和__init__被調用的順序也能從上面代碼的輸出結果中看出:__new__函數首先被調用,構造了一個newStyleClass的實例,接著__init__函數在__new__函數返回一個實例的時候被調用,並且這個實例作為self參數被傳入了__init__函數。
這裡需要注意的是,如果__new__函數返回一個已經存在的實例(不論是哪個類的),__init__不會被調用。如下面代碼所示:
obj = 12
class returnExistedObj(object):
def __new__(cls):
print("__new__ is called")
return obj
def __init(self):
print("__init__ is called")
returnExistedObj()
執行結果如下:
__new__ is called
12
同時另一個需要注意的點是:
如果我們在__new__函數中不返回任何對象,則__init__函數也不會被調用。
如下面代碼所示:
class notReturnObj(object):
def __new__(cls):
print("__new__ is called")
def __init__(self):
print("__init__ is called")
print(notReturnObj())
執行結果如下:
__new__ is called
None
可見如果__new__函數不返回對象的話,不會有任何對象被創建,__init__函數也不會被調用來初始化對象。
總結幾個點__init__不能有返回值
__new__函數直接上可以返回別的類的實例。如上面例子中的returnExistedObj類的__new__函數返回了一個int值。
只有在__new__返回一個新創建屬於該類的實例時當前類的__init__才會被調用。如下面例子所示:
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()
輸出結果為:
__new__ is called
sample
如果對於參考答案有不認同的,大家可以在評論區指出和補充,歡迎留言!
更多題目:
10、說說Python可變與不可變數據類型?
11、說說Python模塊主要分哪三類?
12、列舉Python中的標準異常類?
13、Python中深拷貝與淺拷貝的區別?
14、Python中迭代器和生成器的區別?
15、Python可迭代對象怎麼獲取迭代器?
16、你了解什麼是 Python 之禪麼?
17、說說Python字典以及基本操作?
18、說說Python有幾種字符串格式化?
19、說說Python多線程與多進程的區別?
20、說說HTTP常見響應狀態碼?
21、Python 單引號、雙引號、三引號區別?
22、說說Python中猴子補丁是什麼?
23、說說Python中的垃圾回收機制?
24、Python中有幾種交換兩個變量的值?
25、說說Python中的6種位運算符?
26、說說Python中的類型轉換有哪些?
27、Python中實現二分查找的2種方法?
28、說說Python中的lambda表達式?
29、說說Python中的反射是什麼?
30、Python刪除list的重複元素有幾種方法?
關注小猿公眾號,每天學習一道題