文章目錄
###前言
文章內容爲understanding-new-and-init的譯文。
本文的目的是討論Python中的 __new__ 和 __init__
__new__ 和 __init__ 的區別表現在:1、自身的區別 2、老式類和新式類在Python中的定義
理解 __new__ 和 __init__ 的區別
兩者的主要區別是,__new__ 負責處理對象的創建,__init__ 負責處理對象的初始化。 在對象的實例化過程中,兩個方法在定義不同時,工作方式略有不同。
Python2中的類
Python2中有新式類和老式類之分。老式類出現在Python3問世前, 定義時不繼承基類 object
, 而是默認繼承 type
,新式類則直接顯示繼承 object
。
Python2中的老式類:
class A: # -> inherits from 'type'
pass
Python2中的新式類
class A(object): # -> clearly inherits from 'object'
pass
Python3中沒有新式類和老式類的區分,所有的的類直接繼承自 object
, 因此沒有顯示地指出 object
作爲基類的必要。 object
基類的方法和屬性,通用於所有新式類。
在文章的其它部分,我們將討論 __new__ 和 __init__ 方法在兩種情況下的區別: 它們如何表現和如何使用它們。
處理不同的情況
在深入實際的實現之前,你需要知道 __new__(類方法)接收 cls
作爲第一個參數, __init__ 接收 self
作爲第一個參數。 當調用 __new__ 時, 實際上你還沒有獲得一個實例, 所以此刻 self
並不存在。 而 __init__ 在 __new__ 創建並返回一個實例 之後調用, 此時實例已經創建可用,所以你可以傳遞使用 self
。
老式類中的 __new__ 和 __init__
老式類實際上沒有 __new__ 方法,因爲 __init__ 就是它們的構造函數,所以我們可以嘗試:
class A:
def __new__(cls):
print "A.__new__ is called" # -> this is never called
A()
在這種情況下, __new__ 方法不永遠不會被執行, 因爲它不是老式類目標函數。
如果我們使用重寫 __init__ 的方式來替代:
class A:
def __init__(self):
print "A.__init__ called"
A()
輸出將會是:
A.__init__ called
現在讓我們嘗試讓 __init__ 返回一個值:
class A:
def __init__(self):
return 29
A()
這會導致一個異常:
TypeError: __init__() should return None
這意味着在實例化老式類的時,我們實際上不能控制它的返回。
新式類中的 __new__ 和 __init__
新式類可以讓開發者同時重寫 __new__ 和 __init__ ,兩者有不同的用處, __new__(構造函數) 單一的創建對象,__init__ (初始化函數) 初始化這個對象。
讓我們看下它們的執行順序:
class A(object): # -> don't forget the object specified as base
def __new__(cls):
print "A.__new__ called"
return super(A, cls).__new__(cls)
def __init__(self):
print "A.__init__ called"
A()
輸出如下:
A.__new__ called
A.__init__ called
你也許想要知道 __new__ 和 __init__ 在何處被調用,我能告訴你的是,在調用類名時(實例化期間) __new__ 被自動調用。而 __init__ 則在每次 __new__ 返回類的實例時調用,並將返回的實例作爲 self
參數傳遞給 __init__。 即使你將創建的實例保存成全局或者靜態對象, 並且在每次調用 __new__後返回這個對象,__init__依然每次會被調用。
知道這個意味着如果我們在 __new__ 忽略調用基類的 super(A, cls).__new__(cls)
,會導致 __init__ 不被執行。讓我們看看下面這種情況:
class A(object):
def __new__(cls):
print "A.__new__ called"
def __init__(self):
print "A.__init__ called" # -> is actually never called
print A()
輸出結果爲:
A.__new__ called
None
很明顯,當我們在構造器中不返回東西時,實例被認爲是 None
。
想象下如果我們嘗試讓 __new__ 返回一些東西,會發生什麼事情。
class A(object):
def __new__(cls):
print "A.__new__ called"
return 29
print A()
輸出結果爲:
A.__new__ called
29
讓我們看看當我們嘗試讓 __init__ 返回時,會發生什麼:
class A(object):
def __init__(self):
print "A.__init__ called"
return 33 # -> TypeError: __init__ should return None
A()
觸發了異常:
TypeError: __init__ should return None
這主要是因爲調用 __init__ 的處理程序引發了 TypeError
異常,從 __init__ 返回東西沒有意義,因爲它的主要功能是更改新創建實例的狀態。
看起來新式類給我們提供了更多的靈活性,允許我們在對象創建和初始化的前後做任何操作,並且允許我們控制實例化時返回的內容。
考慮到這一點,讓我們在 __new__ 中返回另一個類的實例。
首先,我們定義一個待用的類:
class Sample(object):
def __str__(self):
return "SAMPLE"
然後, 我們定義一個重寫 __new__ 的類:
class A(object):
def __new__(cls):
return Sample()
這個也可以寫成如下形式:
class A(object):
def __new__(cls):
return super(A, cls).__new__(Sample)
接着調用:
print A()
輸出爲:
SAMPLE
簡單總結:
Python3中 __new__ 負責對象的創建,__init__ 負責對象的初始化。