1. 程式人生 > >python之面向對象(繼承的實現原理及封裝)

python之面向對象(繼承的實現原理及封裝)

let 基類 什麽 isp odi speed utf-8 賦值 類名

一、繼承的實現原理

繼承的順序

技術分享

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()
View Code

方法二: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()
View Code

不用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‘>)
‘‘‘
View Code

使用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‘>)
‘‘‘
View Code

當你使用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是一種特殊的屬性,訪問它時會執行一段功能(函數)然後返回值

python之面向對象(繼承的實現原理及封裝)