當看到一隻鳥走起來像鴨子、游泳像鴨子、叫起來也像鴨子,那麼這隻鳥就能夠被稱爲鴨子python
判斷某一個對象是否有某一個屬性,hasattr()程序員
class Company(object): def __init__(self, employee_list): self.employee = employee_list def __getitem__(self, item): return self.employee[item] def __len__(self): return len(self.employee) print(hasattr(Company, "__len__")) company = Company(['tom', 'bob', 'jane']) print(hasattr(company, "__len__"))
判斷某一個對象是不是某種類型,isinstance()web
對於判斷對象是否能夠調用某函數,更好的方法是去判斷其是不是某種類型,而不是該對象是否有某個屬性。redis
# 在某些狀況下但願斷定某個對象的類型,咱們須要強制某個子類必須實現某些方法 from collections.abc import Sized class Company(object): def __init__(self, employee_list): self.employee = employee_list def __getitem__(self, item): return self.employee[item] def __len__(self): return len(self.employee) company = Company(['tom', 'bob', 'jane']) print(isinstance(company, Sized))
# 設計一個抽象基類,指定子類必須事項某些方法 # 實現一個web框架,集成cache(redis, cache, memorychache) class CacheBase(): def get(self, key): raise NotImplementedError def set(self, key, value): raise NotImplemnetedError class RedisCache(CacheBase): pass redis_cache = RedisCache() redis_cache.set("key", "value")
報錯以下示:算法
# 做以下更改 class RedisCache(CacheBase): def __init__(self): self.dic = {} def get(self, key): return self.dic[key] if key in self.dic else None def set(self, key, value): self.dic[key] = value redis_cache = RedisCache() redis_cache.set("key", "value") print(redis_cache.get("key"))
class A(object): pass class B(A): pass class C(B): pass c = C() print(type(c)) # <class '__main__.C'> print(type(C)) # <class 'type'> print(isinstance(C, B)) # False print(isinstance(C, object)) # True
先實例屬性再類屬性編程
若是把實例當作是類,那麼類變量就是父類中的屬性,而實例變量是子類的屬性,子類中沒有父類有 會從父類中繼承,子類中有父類有 至關於覆蓋重寫
框架
class A: name = 'bb' def __init__(self, x): self.x = x a1 = A(1) a2 = A(2) print(a1.name) #繼承類變量bb print(a1.x) # 就是實例變量x的值 print(A.name) # 類變量bb A.name = 'dd' # 更改了類變量 print(A.name) # 類變量發生更改dd print(a1.name) # 實例的類變量也更改dd a1.name = 'name' print(a1.name) # 在此更改類變量 print(A.name) # 刪除了實例屬性 A.name = 'last' # 在此更改了類變量 del a1.name # 刪除了實例變量 print(a1.name) # 繼承類變量last
類變量和屬性的調用順序:函數
在python中,爲了重用代碼,能夠使用在子類中使用父類的方法,可是在多繼承中可能會出現重複調用的問題,支持多繼承的面向對象編程均可能會致使鑽石繼承(菱形繼承)問題 ,以下示:測試
class A(): def __init__(self): print("進入A…") print("離開A…") class B(A): def __init__(self): print("進入B…") A.__init__(self) print("離開B…") class C(A): def __init__(self): print("進入C…") A.__init__(self) print("離開C…") class D(B, C): def __init__(self): print("進入D…") B.__init__(self) C.__init__(self) print("離開D…") d = D() # 實際上會先找B, 而後找A, 而後是C, 再找A ''' 進入D… 進入B… 進入A… 離開A… 離開B… 進入C… 進入A… 離開A… 離開C… 離開D… '''
鑽石繼承(菱形繼承)會帶來什麼問題?.net
多重繼承容易致使鑽石繼承(菱形繼承)問題,上邊代碼實例化 D 類後咱們發現 A 先後進入了兩次。另外,假設 A 的初始化方法裏有一個計數器,那這樣 D 一實例化,A 的計數器就跑兩次(若是遭遇多個鑽石結構重疊還要更多),很明顯是不符合程序設計的初衷的(程序應該可控,而不能受到繼承關係影響)。
如何避免鑽石繼承(菱形繼承)問題?
爲解決這個問題,Python 使用了一個叫「方法解析順序(Method Resolution Order,MRO)」的東西,還用了一個叫 C3 的算法。
MRO 的順序基本就是:在避免同一類被調用屢次的前提下,使用廣度優先和從左到右的原則去尋找須要的屬性和方法。有則調用,沒有就查找添加到MRO中。
在繼承體系中,C3 算法確保同一個類只會被搜尋一次。例子中,若是一個屬性或方法在 D 類中沒有被找到,Python 就會搜尋 B 類,而後搜索 C類,若是都沒有找到,會繼續搜索 B 的基類 A,若是仍是沒有找到,則拋出「AttributeError」異常。
能夠使用 類名.mro 得到 MRO 的順序(注:object 是全部類的基類,金字塔的頂端):
print(D.mro()) # [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
爲了防止多繼承中的一些問題,使用C3算法解決,它可以保證調用鏈
中全部的類僅出現一次,也就是說每一個節點後面的類都不繼承它,不然將它從調用鏈中刪除,D.mro()
查看調用鏈。
而super()
的使用就是基於這條調用鏈,當使用super()時,會查找調用鏈,找到當前類,調用下一個類,沒有則找下一個。super()默認是當前類,也能夠寫類名
class A(): def __init__(self): print("進入A…") print("離開A…") class B(A): def __init__(self): print("進入B…") super().__init__() print("離開B…") class C(A): def __init__(self): print("進入C…") super().__init__() print("離開C…") class D(B, C): def __init__(self): print("進入D…") super().__init__() print("離開D…") d = D() ''' 進入D… 進入B… 進入C… 進入A… 離開A… 離開C… 離開B… 離開D… '''
MRO的侷限性:類型衝突時, 子類改變了基類的方法搜索順序。而 子類不能改變基類的方法搜索順序。在 Python 2.2 的 MRO 算法中並不能保證這種單調性,它不會阻止程序員寫出上述具備二義性的繼承關係,所以極可能成爲錯誤的根源。
博客:https://mp.weixin.qq.com/s?src=11×tamp=1606794886&ver=2739&signature=SRS5nobqUFoRtzuAsoe8pOknkOYeNxLZz90OnfG3sQtaaMcaI23ZYMSjmGLKogS997A1whxzOgqTugCvuNSlEz5akZ86QE4myAw2JboDp-DRHrsLiWKCj97Ld2lKTq&new=1
mro(Child(Base1, Base2)) = [Child] + merge(mro(Base1), mro(Base2), [Base1, Base2]) (其中Child繼承自Base1, Base2)
mro( B ) = mro( B(A) ) = [B] + merge( mro(A) + [A] ) = [B] + merge( [A] + [A] ) = [B,A]
mro(B) = mro( B(A1, A2, A3 …) ) = [B] + merge( mro(A1), mro(A2), mro(A3) ..., [A1, A2, A3] ) = ...
PS: 計算結果爲列表,列表中至少有一個元素即類本身,如上述示例[A1,A2,A3]。merge操做是C3算法的核心。
(參考博客:https://blog.csdn.net/u011467553/article/details/81437780)
+操做:
list_ = ['A'] + ['B'] print(list_) # ['A', 'B']
如計算merge( [E,O], [C,E,F,O], [C] ) 有三個列表 : list1 list2 list3 1 merge不爲空,取出第一個列表列表list1的表頭E,進行判斷 各個列表的表尾分別是[O], [E,F,O],E在這些表尾的集合中,於是跳過當前當前列表 2 取出列表list2的表頭C,進行判斷 C不在各個列表的集合中,於是將C拿出到merge外,並從全部表頭刪除 merge( [E,O], [C,E,F,O], [C]) = [C] + merge( [E,O], [E,F,O] ) 3 進行下一次新的merge操做 ...... merge( [E,O], [C,E,F,O], [C]) = [C] + merge( [E,O], [E,F,O] ) = [c] + [E] + merge( [O], [F, 0]) = [c] + [E] + [F] + merge( [O], [0]) = [C] + [E] + [F] + [0]
看看以前的
print(D.mro()) # [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>] mro(D(B, C)) = [D] + merge(mro(B(A)), mro(C(A)), [B, C]) = [D] + merge([B] + merge(A(object), [A]), [C] + merge(A(object), [A]), [B, C]) = [D] + merge([B] + merge([A, object], [A]), [C] + merge([A, object], [A]), [B, C]) = [D] + merge([B] + [A, object], [C] + [A, object], [B, C]) = [D] + merge([B, A, object], [C, A, object], [B, C]) = [D] + [B] + merge([A, object], [C, A, object], [C]) = [D] + [B] + [C] + merge([A, object], [A, object]) = [D] + [B] + [C] + [A] + merge([object], [object]) = [D] + [B] + [C] + [A] + [object] = [D, B, C, A, object] = [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
# 備註:O==object, 如何計算mro(A) ? mro(A) = mro(A(B, C)) = [A] + merge(mro(B(D, E)), mro(C(E, F)), [B, C]) = [A] + merge([B] + merge(mro(D(O)), mro(E(O)), [D, E]), [C] + merge(mro(E(O)), mro(F(O)), [E, F]), [B, C]) = [A] + merge([B] + merge([D, O], [E, O], [D, E]), [C] + merge([E, O], [F, O], [E, F]), [B, C]) = [A] + merge([B] + [D, E, 0], [C] + [E, F, O], [B, C]) = [A] + merge([B, D, E, 0], [C, E, F, O], [B, C]) merge([D, O], [E, O], [D, E]) = [D] + merge([O], [E, O], [E]) = [D] + [E] + merge([O], [O]) = [D] + [E] + [O] = [D, E, 0] merge([E, O], [F, O], [E, F]) = [E] + merge([O], [F, O], [F]) = [E] + [F] + merge([O], [O]) = [E] + [F] + [O] = [E, F, O] = [A] + [B] + merge([D, E, 0], [C, E, F, O], [C]) = [A] + [B] + [D] + merge([E, 0], [C, E, F, O], [C]) = [A] + [B] + [D] + [C] + merge([E, 0], [E, F, O]) = [A] + [B] + [D] + [C] + [E] + merge([0], [F, O]) = [A] + [B] + [D] + [C] + [E] + [F]+ merge([0], [O]) = [A] + [B] + [D] + [C] + [E] + [F]+ [O] = [A, B, D, C, E, F, O]
# 以上案例的代碼測試 class D: pass class E: pass class F: pass class B(D,E): pass class C(E,F): pass class A(B,C): pass print("從A開始查找:") for s in A.__mro__: print(s) print("從B開始查找:") for s in B.__mro__: print(s) print("從C開始查找:") for s in C.__mro__: print(s) ''' 從A開始查找: <class '__main__.A'> <class '__main__.B'> <class '__main__.D'> <class '__main__.C'> <class '__main__.E'> <class '__main__.F'> <class 'object'> 從B開始查找: <class '__main__.B'> <class '__main__.D'> <class '__main__.E'> <class 'object'> 從C開始查找: <class '__main__.C'> <class '__main__.E'> <class '__main__.F'> <class 'object'> '''
規律總結: 「從一至頂,有子先出」
結果參考: