一、類的繼承
- 面向對象三要素之一,繼承Inheritance
- class Cat(Animal)這種形式就是從父類繼承,繼承可以讓子類從父類獲取特征(屬性和方法)
- 在面向對象的世界中,從父類繼承,就可以直接擁有父類的屬性和方法,這樣可以減少代碼,多復用,子類可以定義自己的屬性和方法
class Animal: def __init__(self,name): self._name = name def shout(self): print('{} shout'.format(self.__class__.__name__)) @property #屬性裝飾器 def name(self): return self._name a = Animal('monster') a.shout() class Cat(Animal): pass cat = Cat('garfield') cat.shout() print(cat.name) 父類: Animal就是Cat的父類,也稱為基類,超類 子類:Cat就是Animal的子類,也稱為派生類 如果類定義時,沒有基類列表,等同於繼承自object,在python3中,object類是所有對象的根基類
1、查看繼承的特殊屬性和方法
- __base__ : 類的基類
- __based__ : 類的基類元組
- __mro__ : 顯示方法查找順序,基類的元組
- mro()方法 : 顯示方法查找順序,基類的元組
- __subclasses__() : 類的子類列表
2、繼承中的訪問控制
- 從父類繼承,自己沒有的就可以到父類中找
- 私有的都是不可以訪問的,但是本質上依然是改了名稱放在這個屬性所在類的__dict_-中
- 繼承時,公有的,子類和實例都可以隨意訪問,私有成員被隱藏了,子類和實例不可直接訪問
- 當私有變量所在的類內的方法中可以訪問這個私有變量
- 屬性查找屬性:實例的dict--》類的dict--》父類dict
舉例: class Animal: __COUNT = 0 HEIGHT = 0 def __init__(self,age,weight,height): self.__COUNT += 1 self.age = age self.__weight = weight self.HEIGHT = height def eat(self): print('{} eat'.format(self.__class__.__name__)) def __getweight(self): print(self.__weight) @classmethod def showcount1(cls): print(cls.__COUNT) @classmethod def __showcount2(cls): print(cls.__COUNT) class Cat(Animal): NAME = 'CAT' c = Cat(3,5,15) c.eat() print(c.HEIGHT) #print(c.__COUNT) #私有的不可訪問 #c.__showweight() #私有的不可訪問 c.showcount1() #c.__showcount2() #私有的不可訪問 print(c.NAME) print("{}".format(Animal.__dict__)) print("{}".format(Cat.__dict__)) print(c.__dict__) print(c.__class__.mro())
3、方法的重寫、覆蓋overrride
class Animal: def shout(self): print('Animal shout') class Cat(Animal): #覆蓋了父類方法 def shout(self): print('miao') #覆蓋子類自身的方法,顯示調用了父類的方法 def shout(self): print(super()) print(super(Cat,self)) super().shout() super(Cat,self).shout() c = Cat() c.shout()
4、繼承中的初始化
class A: def __init__(self,a): self.a = a class B(A): def __init__(self,b,c): self.b = b self.c = c def printv(self): print(self.b) print(self.a) # 報錯 f = B(200,300) print(f.__dict__) print(f.__class__.__bases__) f.printv() 上例代碼可知:如果類B定義時聲明繼承自類A,則在類B中__bases__中是可以看到類A,但是這和是否調用類A的構造方法是兩回事
1、如果子類B沒有定義init方法,初始化的時候會自動調用基類A是init方法 class A: def __init__(self): self.a1 = 'a1' self.__a2 = 'a2' print('A init') class B(A): pass b = B() print(b.__dict__) 2、如果子類B定義了init方法,實例的初始化不會調用父類的初始化init方法 class A: def __init__(self): self.a1 = 'a1' self.__a2 = 'a2' print('A init') class B(A): def __init__(self): self.b1 = 'b1' print('B init') b = B() print(b.__dict__) 3、通過上面分析,子類不會調用父類init方法,這導致沒有實現繼承效果,所以在子類的__init__方法中, 應該顯式調用父類的__init__方法 class Animal: def __init__(self,age): print('Animal init') self.age = age def show(self): print(self.age) class Cat(Animal): def __init__(self,age,weight): #調用父類的__init__方法順序決定着show方法的結果 super().__init__(age) print('Cat init') self.age = age + 1 self.weight = weight c = Cat(10,5) c.show() 注意、調用父類的__init__方法,出現在不同的位置,
二、多繼承
- OCP原則:多繼承,少修改;繼承的用途:增強基類,實現多
1、多態
- 在面向對象中,父類,子類通過繼承聯系在一起,如果可以通過一套方法,就可以實現不同的表現,就是多態
- 一個類繼承自多個類就是多繼承,它將具有多個類的特征
2、多繼承弊端
- 多繼承很好的模擬了世界,因為事物很少是單一繼承,但是舍棄簡單,必然引入復雜性帶來沖突
- 如同一個孩紙繼承了來自父母雙方的特征
- 多繼承的實現會導致編譯器設計的復雜度增加,所以現在很多語言也舍棄了類的多繼承
3、python多繼承實現
- 多繼承帶來路徑選擇問題,究竟繼承哪個父類的特征
- python使用MRO(method resolution order)解決基類搜索順序問題
4、多繼承的缺點
- 當類很多,繼承復雜的情況下,繼承路徑太多,很難說清楚什么樣的繼承路徑
- python語法是允許多繼承,但是python代碼是解釋執行,只有執行到的時候才發現錯誤
舉例: class Printable: def _print(self): print(111,self.content) class Document: #第三方庫,不允許修改 def __init__(self,content): self.content = content class Word(Document): pass #第三方庫,不允許修改 class Pdf(Document): pass #第三方庫,不允許修改 class PrintableWord(Printable,Word): pass print(222,PrintableWord.__dict__) print(333,PrintableWord.mro()) pw = PrintableWord('test string') pw._print()
5、用裝飾器增強
- 用裝飾器增強一個類,把功能給類附加上去,哪個類需要,就裝飾它
- 優點:簡單方便,在需要的地方動態增加,直接使用裝飾器
def printable(cls): def _print(self): print(self.content,'裝飾器') cls.print = _print return cls class Document: #第三方庫,不允許修改 def __init__(self,content): self.content = content class Word(Document): pass #第三方庫,不允許修改 class Pdf(Document): pass #第三方庫,不允許修改 @printable #先繼承,后裝飾 class PrintableWord(Word): pass print(PrintableWord.__dict__) print(PrintableWord.mro()) pw = PrintableWord('test string') pw.print()
6、Mixin的引用
- Mixin本質上就是多繼承實現的,是一種組合的設計模式
- 在面向對象的設計中,一個復雜的類往往需要很多功能,而這些功能有來自不同的類提供,這就需要很多的類組合在一起
- 從設計的模式的角度來說,多組合,少繼承
舉例: class PrintableMixin: def print(self): print(self.content,'Mixin') def printable(cls): def _print(self): print(self.content,'裝飾器') cls.print = _print return cls class Document: #第三方庫,不允許修改 def __init__(self,content): self.content = content class Word(Document): pass #第三方庫,不允許修改 class Pdf(Document): pass #第三方庫,不允許修改 class PrintableWord(PrintableMixin,Word): pass print(PrintableWord.__dict__) print(PrintableWord.mro()) pw = PrintableWord('test string') pw.print() Mixin就是其他類混合進來,同時帶來了類的屬性和方法,這里看來Mixin類和裝飾器效果一樣,也沒有什么特別 但是Mixin是類,就可以繼承
7、Mixin類的使用原則
- Mixin類中不應該顯式的出現__init__初始化方法
- Mixin類通常不能獨立工作,因為它是准備混入別的類中的部分功能實現
- Mixin類的祖先類也應是Mixin類,使用時,Mixin類通常在繼承列表的第一個位置