🍔類的菱形繼承問題

一.類的分類

在前面一章咱們簡單介紹了一些類的分類java

1.新式類

  • 繼承了 object 的類以及該類的子類, 都是新式類 (Python3中統一都是新式類)
  • Python3 中若是一個類沒有繼承任何類, 則默認會繼承 object 類, 也就是Python3中全部的類都是新式類
🍔在"Python3"中
class Default:  # 默認繼承"object"
    pass

print(Default.__bases__)  # (<class 'object'>,)

2.經典類

  • 沒有繼承 object 的類以及該類的子類, 都是經典類 (只有Python2中才區分新式類和經典類)
  • Python2 中若是一個類沒有繼承任何類, 它不會繼承object 類, 因此Python2 中才有經典類
🍔在"Python2"中
class Default(object):  # 新式類
    pass

class Animal:  # 經典類
    pass

🍔"Python2"中"print"語法
print Default.__bases__  # (<class 'object'>,)
print Animal.__bases__   # ()

ps : 新式類與經典類的屬性查找順序是不同的python


二.菱形繼承問題

上面說到Python支持多繼承, 但新式類與經典類的屬性查找順序是不同的算法

java語言中,它不支持多繼承,只支持繼承一個父類python2.7

python語言,支持多繼承,好比 A(B,C,D)code

1.非菱形結構

  • 非菱形結構下, 經典類與新式類的屬性查找順序是同樣的繼承

  • 若是繼承關係爲非菱形結構,則會按照先找 B 這一條分支,而後再找 C 這一條分支,最後找 D 這一條分支的順序直到找到咱們想要的屬性utf-8

  • 非菱形結構 (兩條查詢路徑最後分別繼承了 F1 和 F4)

class F1:
    def s1(self):
        print('F1:s1')
    def s2(self):
        print('F1:s2')

class F2(F1):
    # def s1(self):
    #     print('F2:s1')
    def s2(self):
        print('F2:s2')

class F4():
    def s1(self):
        print('F4:s1')
    def s2(self):
        print('F4:s2')

class F3(F2,F4):
    def s1(self):
        super().s1()  # 調用父類的s1方法,到底使用了哪一個父類的s1
    def s2(self):
        print('F3:s2')


f1 = F3()
f1.s1()  # F1:s1
  • 查找順序 : F3 ===> F2 ===> F1

2.菱形結構

  • 若是繼承關係爲菱形結構,即子類的父類最後繼承了同一個類,那麼屬性的查找方式有兩種:
    • 新式類----->廣度優先
    • 經典類----->深度優先

一、新式類----->廣度優先

  • 不找多各種最後繼承的同一個類,直接去找下一個父類
  • __mro__ : 只有新式類纔有的屬性, 可查看屬性查找的順序

img

class G(object):
    # def test(self):
    #     print('from G')
    pass

class E(G):
    # def test(self):
    #     print('from E')
    pass

class B(E):
    # def test(self):
    #     print('from B')
    pass

class F(G):
    # def test(self):
    #     print('from F')
    pass

class C(F):
    # def test(self):
    #     print('from C')
    pass

class D(G):
    # def test(self):
    #     print('from D')
    pass

class A(B, C, D):
    def test(self):
        print('from A')

obj = A()
obj.test()
print(A.__mro__)
'''查找順序
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.G'>, <type 'object'>)
'''

二、經典類----->深度優先

  • 從左到右選路, 一條路走到黑

img

#coding:utf-8
# python2 解釋器

class G():
    pass
    def test(self):
        print('G---test')

class F(G):
    pass
    # def test(self):
    #     print('FFF---test')
class E(G):
    pass
    # def test(self):
    #     print('EEE---test')
class D(G):
    pass
    # def test(self):
    #     print('DDD---test')
class B(E):
    pass
    # def test(self):
    #     print('BBB---test')
class C(F):
    pass
    # def test(self):
    #     print('CCC---test')

class A(B,C,D):
    pass
    # def test(self):
    #     print('AAA---test')

a=A()
a.test()  # G---test

三.小示例

1.新式類

  • 菱形結構示例 (子類最後繼承了一樣的類, 都繼承了 A1 和 A2), 能夠當作兩個菱形

4d1651e9189743b96c356a9fc9054ef

class A1:
    def foo(self):
        print('A1_foo')

class A2:
    def foo(self):
        print("A2_foo")

    def bar(self):
        print("A2_bar")

class B1(A1, A2):
    pass

class B2(A1, A2):
    def bar(self):
        print("B2_bar")

class C(B1, B2):
    pass

c = C()
c.foo()  # A1_foo
c.bar()  # B2_bar

print(C.__mro__)
'''輸出
(<class '__main__.C'>, <class '__main__.B1'>, <class '__main__.B2'>, <class '__main__.A1'>, <class '__main__.A2'>, <class 'object'>)
'''
  • c.foo() 的查找順序 : C ==> B1 ===> B2 ==> A1
  • c.bar() 的查找順序 : C ==> B1 ==> B2

2.經典類

d415228a749e9175eaa2aea308686a7

#coding:utf-8
# python2.7 解釋器
class A1():
    def foo(self):
        print 'A1_foo'

class A2():
    def foo(self):
        print "A2_foo"

    def bar(self):
        print "A2_bar"

class B1(A1, A2):
    pass

class B2(A1, A2):
    def bar(self):
        print "B2_bar"

class C(B1, B2):
    pass

c = C()
c.foo()  # A1_foo
c.bar()  # A2_bar
  • c.foo() 的查找順序 : C ==> B1 ===> A1
  • c.bar() 的查找順序 : C ==> B1 ==> A1 ===> A2

四. c3 算法與 mro

1.簡介

爲了實現繼承,python會在MRO列表上從左到右開始查找基類,直到找到第一個匹配這個屬性的類爲止數學

而這個MRO列表的構造是經過一個C3線性化算法來實現的class

咱們不去深究這個算法的數學原理,它實際上就是合併全部父類的MRO列表並遵循以下三條準則 :test

  • 子類會先於父類被檢查
  • 多個父類會根據它們在列表中的順序被檢查
  • 若是對下一個類存在兩個合法的選擇,選擇第一個父類

2.__mro__ 方法使用

其實上面咱們已經使用了這種方法, 下面再補充一點

  • 只有新式類纔可使用 (Python2中的類若是沒繼承object就沒法使用)
  • [類名]__mro__ : 打印屬性查找順序, 是一個元組
  • [類名].mro() : 打印屬性查找順序, 是一個列表
🍔Python2 中
#coding:utf-8

class C(object):  # 繼承object類,若是不繼承,調用 mro 方法會報錯 
    def run(self):
        print "run_C"

class B(C):
    pass

class A(B):
    pass

a = A()
a.run()  # run_C
print A.__mro__  
# (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <type 'object'>)

print A.mro()  
# [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <type 'object'>]

🍔Python3 中
class C:  # 已經自動繼承了object類
    def run(self):
        print("run_C")

class B(C):
    pass

class A(B):
    pass

a = A()
a.run()  # run_C
print(A.__mro__)
# (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>)

print(A.mro())
# [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>]
相關文章
相關標籤/搜索