python2中的__new__與__init__,新式類和經典類

pythontab發表於2014-08-01

在python2.x中,從object繼承得來的類稱為新式類(如class A(object))不從object繼承得來的類稱為經典類(如class A())

新式類跟經典類的差別主要是以下幾點:

  1. 新式類物件可以直接透過__class__屬性獲取自身型別:type

  2. 繼承搜尋的順序發生了改變,經典類多繼承時屬性搜尋順序: 先深入繼承樹左側,再返回,開始找右側(即深度優先搜尋);新式類多繼承屬性搜尋順序: 先水平搜尋,然後再向上移動

例子:

經典類: 搜尋順序是(D,B,A,C)

>>> class A: attr = 1
...
>>> class B(A): pass
...
>>> class C(A): attr = 2
...
>>> class D(B,C): pass
...
>>> x = D()
>>> x.attr
1

新式類繼承搜尋程式是寬度優先

新式類:搜尋順序是(D,B,C,A)

>>> class A(object): attr = 1
...
>>> class B(A): pass
...
>>> class C(A): attr = 2
...
>>> class D(B,C): pass
...
>>> x = D()
>>> x.attr
2

  3. 新式類增加了__slots__內建屬性, 可以把例項屬性的種類鎖定到__slots__規定的範圍之中。

  4. 新式類增加了__getattribute__方法

  5.新式類內建有__new__方法而經典類沒有__new__方法而只有__init__方法

注意:Python 2.x中預設都是經典類,只有顯式繼承了object才是新式類

     而Python 3.x中預設都是新式類(也即object類預設是所有類的祖先),不必顯式的繼承object(可以按照經典類的定義方式寫一個經典類並分別在python2.x和3.x版本中使用dir函式檢驗下。

例如:class A():

      pass

    print(dir(A))

會發現在2.x下沒有__new__方法而3.x下有。

接下來說下__new__方法和__init__的區別:

在python中建立類的一個例項時,如果該類具有__new__方法,會先呼叫__new__方法,__new__方法接受當前正在例項化的類作為第一個引數(這個引數的型別是type,這個型別在c和python的互動程式設計中具有重要的角色,感興趣的可以搜下相關的資料),其返回值是本次建立產生的例項,也就是我們熟知的__init__方法中的第一個引數self。那麼就會有一個問題,這個例項怎麼得到?

注意到有__new__方法的都是object類的後代,因此如果我們自己想要改寫__new__方法(注意不改寫時在建立例項的時候使用的是父類的__new__方法,如果父類沒有則繼續上溯)可以透過呼叫object的__new__方法類得到這個例項(這實際上也和python中的預設機制基本一致),如:

class display(object):
    def __init__(self, *args, **kwargs):
        print("init")
    def __new__(cls, *args, **kwargs):
        print("new")
        print(type(cls))
        return object.__new__(cls, *args, **kwargs)   
a=display()

執行上述程式碼會得到如下輸出:

new

<class 'type'>

init

因此我們可以得到如下結論:

在例項建立過程中__new__方法先於__init__方法被呼叫,它的第一個引數型別為type。

如果不需要其它特殊的處理,可以使用object的__new__方法來得到建立的例項(也即self)。

於是我們可以發現,實際上可以使用其它類的__new__方法類得到這個例項,只要那個類或其父類或祖先有__new__方法。

class another(object):
    def __new__(cls,*args,**kwargs):
        print("newano")
        return object.__new__(cls, *args, **kwargs)   
class display(object):
    def __init__(self, *args, **kwargs):
        print("init")
    def __new__(cls, *args, **kwargs):
        print("newdis")
        print(type(cls))
        return another.__new__(cls, *args, **kwargs)   
a=display()

上面的輸出是:

newdis
<class 'type'>
newano
init

所有我們發現__new__和__init__就像這麼一個關係,__init__提供生產的原料self(但並不保證這個原料來源正宗,像上面那樣它用的是另一個不相關的類的__new__方法類得到這個例項),而__init__就用__new__給的原料來完善這個物件(儘管它不知道這些原料是不是正宗的)


相關文章