python中的多繼承(經典類和新式類)
python和C++一樣,支援多繼承。概念雖然容易,但是困難的工作是如果子類呼叫一個自身沒有定義的屬性,它是按照何種順序去到父類尋找呢,尤其是眾多父類中有多個都包含該同名屬性。
- class P1 #(object):
- def foo(self):
- print 'p1-foo'
- class P2 #(object):
- def foo(self):
- print 'p2-foo'
- def bar(self):
- print 'p2-bar'
- class C1 (P1,P2):
- pass
- class C2 (P1,P2):
- def bar(self):
- print 'C2-bar'
- class D(C1,C2):
- pass
對經典類和新式類來說,屬性的查詢順序是不同的。現在我們分別看一下經典類和新式類兩種不同的表現
1、經典類
- d=D()
- d.foo() # 輸出 p1-foo
- d.bar() # 輸出 p2-bar
例項d呼叫foo()時,搜尋順序是 D => C1 => P1
例項d呼叫bar()時,搜尋順序是 D => C1 => P1 => P2
換句話說,經典類的搜尋方式是按照“從左至右,深度優先”的方式去查詢屬性。d先查詢自身是否有foo方法,沒有則查詢最近的父類C1裡是否有該方法,如果沒有則繼續向上查詢,直到在P1中找到該方法,查詢結束。
2、新式類
使用新式類要去掉第一段程式碼中的註釋
- d=D()
- d.foo() # 輸出 p1-foo
- d.bar() # 輸出 c2-bar
例項d呼叫foo()時,搜尋順序是 D => C1 => C2 => P1
例項d呼叫bar()時,搜尋順序是 D => C1 => C2
可以看出,新式類的搜尋方式是採用“廣度優先”的方式去查詢屬性。
在python3中類已經新增(object)
出處:https://www.cnblogs.com/linyawen/archive/2012/04/25/2469538.html
補充:
從Python2.2開始,Python 引入了 new style class(新式類)
新式類跟經典類的差別主要是以下幾點:
新式類物件可以直接通過__class__屬性獲取自身型別:type
- # -*- coding:utf-8 -*-
- class E:
- #經典類
- pass
- class E1(object):
- #新式類
- pass
- e = E()
- print"經典類"
- print e
- print type(e)
- print e.__class__
- print"新式類"
- e1 = E1()
- print e1
- print e1.__class__
- print type(e1)
- 經典類
- <__main__.E instance at 0x0000000002250B08>
- <type 'instance'>
- __main__.E
- 新式類
- <__main__.E1 object at 0x0000000002248710>
- <class'__main__.E1'>
- <class'__main__.E1'>
我使用的是python 2.7。
E1是定義的新式類。那麼輸輸出e1的時候,不論是type(e1),還是e1.__class__都是輸出的<class '__main__.E1'>。
2. 繼承搜尋的順序發生了改變,經典類多繼承屬性搜尋順序: 先深入繼承樹左側,再返回,開始找右側;新式類多繼承屬性搜尋順序: 先水平搜尋,然後再向上移動
- # -*- coding:utf-8 -*-
- class A(object):
- """
- 新式類
- 作為所有類的基類
- """
- def foo(self):
- print"class A"
- class A1():
- """
- 經典類
- 作為所有類的基類
- """
- def foo(self):
- print"class A1"
- class C(A):
- pass
- class C1(A1):
- pass
- class D(A):
- def foo(self):
- print"class D"
- class D1(A1):
- def foo(self):
- print"class D1"
- class E(C, D):
- pass
- class E1(C1, D1):
- pass
- e = E()
- e.foo()
- e1 = E1()
- e1.foo()
輸出
- class D
- class A1
因為A新式類,對於繼承A類都是新式類,首先要查詢類E中是否有foo(),如果沒有則按順序查詢C->D->A。它是一種廣度優先查詢方式。
因為A1經典類,對於繼承A1類都是經典類,首先要查詢類E1中是否有foo(),如果沒有則按順序查詢C1->A1->D1。它是一種深度優先查詢方式。
3. 新式類增加了__slots__內建屬性, 可以把例項屬性的種類鎖定到__slots__規定的範圍之中。
比如只允許對A例項新增name和age屬性:
- # -*- coding:utf-8 -*-
- class A(object):
- __slots__ = ('name', 'age')
- class A1():
- __slots__ = ('name', 'age')
- a1 = A1()
- a = A()
- a1.name1 = "a1"
- a.name1 = "a"
A是新式類添加了__slots__ 屬性,所以只允許新增 name age
A1經典類__slots__ 屬性沒用,
- Traceback (most recent call last):
- File "t.py", line 13, in <module>
- a.name1 = "a"
- AttributeError: 'A' object has no attribute 'name1'
所以a.name是會出錯的
通常每一個例項都會有一個__dict__屬性,用來記錄例項中所有的屬性和方法,也是通過這個字典,可以讓例項繫結任意的屬性
而__slots__屬性作用就是,當類C有比較少的變數,而且擁有__slots__屬性時,
類C的例項 就沒有__dict__屬性,而是把變數的值存在一個固定的地方。如果試圖訪問一個__slots__中沒有
的屬性,例項就會報錯。這樣操作有什麼好處呢?__slots__屬性雖然令例項失去了繫結任意屬性的便利,
但是因為每一個例項沒有__dict__屬性,卻能有效節省每一個例項的記憶體消耗,有利於生成小而精
乾的例項。
4. 新式類增加了__getattribute__方法
- class A(object):
- def __getattribute__(self, *args, **kwargs):
- print"A.__getattribute__"
- class A1():
- def __getattribute__(self, *args, **kwargs):
- print"A1.__getattribute__"
- a1 = A1()
- a = A()
- a.test
- print"========="
- a1.test
- A.__getattribute__
- =========
- Traceback (most recent call last):
- File "t.py", line 18, in <module>
- a1.test
- AttributeError: A1 instance has no attribute 'test'
可以看出A是新式類,每次通過例項訪問屬性,都會經過__getattribute__函式,
A1不會呼叫__getattribute__所以出錯了
Python 2.x中預設都是經典類,只有顯式繼承了object才是新式類
Python 3.x中預設都是新式類,不必顯式的繼承object
出處:https://blog.csdn.net/u010066807/article/details/46896835