一、繼承的實現原理
繼承的順序
class A(object): def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D,E): # def test(self): # print('from F') pass f1=F() f1.test() print(F.__mro__) #只有新式才有這個屬性可以查看線性列表,經典類沒有這個屬性 #新式類繼承順序:F->D->B->E->C->A #經典類繼承順序:F->D->B->A->E->C #python3中統一都是新式類 #pyhon2中才分新式類與經典類
繼承原理
python到底是如何實現繼承的,對於你定義的每一個類,python會計算出一個方法解析順序(MRO)列表,這個MRO列表就是一個簡單的所有基類的線性順序列表,例如
>>> F.mro() #等同於F.__mro__ [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>,
<class 'object'>]
子類繼承了父類的方法,然后想進行修改,注意了是基於原有的基礎上修改,那么就需要在子類中調用父類的方法
方法一:父類名.父類方法()

#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' class Vehicle: #定義交通工具類 Country='China' def __init__(self,name,speed,load,power): self.name=name self.speed=speed self.load=load self.power=power def run(self): print('開動啦...') class Subway(Vehicle): #地鐵 def __init__(self,name,speed,load,power,line): Vehicle.__init__(self,name,speed,load,power) self.line=line def run(self): print('地鐵%s號線歡迎您' %self.line) Vehicle.run(self) line13=Subway('中國地鐵','180m/s','1000人/箱','電',13) line13.run()
方法二:super()

class Vehicle: #定義交通工具類 Country='China' def __init__(self,name,speed,load,power): self.name=name self.speed=speed self.load=load self.power=power def run(self): print('開動啦...') class Subway(Vehicle): #地鐵 def __init__(self,name,speed,load,power,line): #super(Subway,self) 就相當於實例本身 在python3中super()等同於super(Subway,self) super().__init__(name,speed,load,power) self.line=line def run(self): print('地鐵%s號線歡迎您' %self.line) super(Subway,self).run() class Mobike(Vehicle):#摩拜單車 pass line13=Subway('中國地鐵','180m/s','1000人/箱','電',13) line13.run()
不用super引發的慘案

#每個類中都繼承了且重寫了父類的方法 class A: def __init__(self): print('A的構造方法') class B(A): def __init__(self): print('B的構造方法') A.__init__(self) class C(A): def __init__(self): print('C的構造方法') A.__init__(self) class D(B,C): def __init__(self): print('D的構造方法') B.__init__(self) C.__init__(self) f1=D() print(D.__mro__) ''' D的構造方法 B的構造方法 A的構造方法 C的構造方法 A的構造方法 (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) '''
使用super()的結果

class A: def __init__(self): print('A的構造方法') class B(A): def __init__(self): print('B的構造方法') super().__init__() #super(B,self).__init__() class C(A): def __init__(self): print('C的構造方法') super().__init__() #super(C,self).__init__() class D(B,C): def __init__(self): print('D的構造方法') super().__init__() #super(D,self).__init__() # C.__init__(self) f1=D() print(D.__mro__) ''' D的構造方法 B的構造方法 C的構造方法 A的構造方法 (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) '''
當你使用super()函數時,Python會在MRO列表上繼續搜索下一個類。只要每個重定義的方法統一使用super()並只調用它一次,那么控制流最終會遍歷完整個MRO列表,每個方法也只會被調用一次(注意注意注意:使用super調用的所有屬性,都是從MRO列表當前的位置往后找,千萬不要通過看代碼去找繼承關系,一定要看MRO列表)
二、封裝
1、要封裝什么
封裝數據和方法
2、為什么要封裝
封裝不是單純意義的隱藏:
1:封裝數據的主要原因是:保護隱私
2:封裝方法的主要原因是:隔離復雜度
3、封裝分為兩個層面
封裝其實分為兩個層面,但無論哪種層面的封裝,都要對外界提供好訪問你內部隱藏內容的接口(接口可以理解為入口,有了這個入口,使用者無需且不能夠直接訪問到內部隱藏的細節,只能走接口,並且我們可以在接口的實現上附加更多的處理邏輯,從而嚴格控制使用者的訪問。
第一個層面的封裝(什么都不用做):創建類和對象會分別創建二者的名稱空間,我們只能用類名.或者obj.的方式去訪問里面的名字,這本身就是一種封裝。
注意:對於這一層面的封裝(隱藏),類名.和實例名.就是訪問隱藏屬性的接口
第二個層面的封裝:類中把某些屬性和方法隱藏起來(或者說定義成私有的),只在類的內部使用、外部無法訪問,或者留下少量接口(函數)供外部訪問。
在python中用雙下划線的方式實現隱藏屬性(設置成私有的)
類中所有雙下划線開頭的名稱如__x都會自動變形成:_類名__x的形式:
class Teacher: def __init__(self,name,age): self.__name=name self.__age=age def tell_info(self): print('姓名:%s,年齡:%s' %(self.__name,self.__age)) def set_info(self,name,age): if not isinstance(name,str): raise TypeError('姓名必須是字符串類型') if not isinstance(age,int): raise TypeError('年齡必須是整型') self.__name=name self.__age=age t=Teacher('egon',18) t.tell_info() t.set_info('egon',19) t.tell_info()
這種自動變形的特點:
1.類中定義的__x只能在內部使用,如self.__x,引用的就是變形的結果。
2.這種變形其實正是針對外部的變形,在外部是無法通過__x這個名字訪問到的。
2.在子類定義的__x不會覆蓋在父類定義的__x,因為子類中變形成了:_子類名__x,而父類中變形成了:_父類名__x,即雙下滑線開頭的屬性在繼承給子類時,子類是無法覆蓋的。
注意:對於這一層面的封裝(隱藏),我們需要在類中定義一個函數(接口函數)在它內部訪問被隱藏的屬性,然后外部就可以使用了。
這種變形需要注意的問題是:
1.這種機制也並沒有真正意義上限制我們從外部直接訪問屬性,知道了類名和屬性名就可以拼出名字:_類名__屬性,然后就可以訪問了,如a._A__N
2.變形的過程只在類的定義是發生一次,在定義后的賦值操作,不會變形
三、propety函數的應用
# class Peopel: # def __init__(self,name,age,sex,weight,height,permission=False): # self.__name=name # self.__age=age # self.__sex=sex # self.__height=height # self.__weight=weight # self.permission=permission # @property # def info(self): # print(""" # -----%s info----- # name:%s # age:%s # sex:%s # weight:%s # height:%s # """%(self.__name,self.__name,self.__age,self.__sex,self.__height,self.__weight)) # @property # def bmi(self): # res=self.__weight / (self.__height ** 2) # return res # @property # def name(self): # return self.__name # @name.setter # def name(self,val): # if not isinstance(val,str): # raise TypeError('must be str') # self.__name=val # @name.deleter # def name(self): # if not self.permission: # raise PermissionError('不讓刪') # del self.__name # egon=Peopel('egon',18,'male',80,1.75) # egon.info # print(egon.bmi) # print(egon.name)
property是一種特殊的屬性,訪問它時會執行一段功能(函數)然后返回值