深入理解__init__和__new__


###前言

文章內容爲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__ 負責對象的初始化。

參考文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章