1. 程式人生 > >python的三大特性之封裝

python的三大特性之封裝

封裝

隱藏物件的某些屬性和實現的細節,僅僅只對外提供公共訪問的方式。將函式和屬性裝到了一個非全域性的名稱空間。

封裝的好處
(1)將變化隔離

(2)便於使用

(3)提高複用性

(4)提高安全性

封裝原則

(1)將不需要對外提供的內容全部都隱藏起來

(2)吧屬性都隱藏,提供公共方法對其訪問

私有變數和私有方法

私有變數:不能在類的外面去引用它。
它依然存在於__dict__中,我們仍然可以呼叫到。只是python對其的名字進行了修改: _類名__名字。但是在類的外部呼叫 :需要“_類名__名字”去使用,在類的內部可以正常的使用名字

在python中定義一個私有的名字 :使用兩條下劃線開頭的方式來定義變數名稱 __N = ‘aaa’

class Student:
    __id = 0   #變成了私有的靜態變數類的資料屬性就應該是
               # 共享的,但是語法上是可以把類的資料屬性設定成私有的如__N,會變形為_A__N
    def __init__(self, name, age):
        self.name = name  #變形為self._Student__name ,私有屬性
        self.age = age    #變形為self._Student__age ,私有屬性
    def func(self): 
         print(Student.__id) #在類的內部使用正常
s = Student('hh', 18) 
s.func() 
print(Student.__id) # 在類的外部直接使用 報錯

print(Student._Student__id) #不報錯

由此,私有變數只能在類的內部訪問的到,外部不能訪問,但是,由於它是一個變形的方式。其實可以通過變形以後的方式進行訪問,但嚴格意義上來講,我們不能通過它來訪問,這時只有我們程式設計師知道就可以了

這種自動變形的特點:

1.類中定義的__x只能在內部使用,如self.__x,引用的就是變形的結果。

2.這種變形其實正是針對外部的變形,在外部是無法通過__x這個名字訪問到的。

3.在子類定義的__x不會覆蓋在父類定義的__x,因為子類中變形成了:_子類名__x,而父類中變形成了:_父類名__x,即雙下滑線開頭的屬性在繼承給子類時,子類是無法覆蓋的。

變形需要注意的問題是:

1.這種機制也並沒有真正意義上限制我們從外部直接訪問屬性,知道了類名和屬性名就可以拼出名字:_類名__屬性,然後就可以訪問了,如a._A__N

2.變形的過程只在類的內部生效,在定義後的賦值操作,不會變形

私有方法

私有方法的定義和私有變數一樣,只需要在前面加上雙下劃線,就是標誌著私有方法

class Student:
    def func(self):
        print("我是一個好學生")  # 在類的內部使用正常
    def __ADCa(self):
        print("我喜歡喝ADCa")
s = Student()
s.func()               #普通方法呼叫
s._Student__ADCa()          #私有方法呼叫
s.__ADCa                       #這樣呼叫,直接報錯

在繼承中,父類如果不想讓子類覆蓋自己的方法,可以將方法定義為私有的

#正常情況,方法都是普通方法

class Dad:
    def dear(self):
        print('from Dad')
    def father(self):
        self.dear()
 
class Son(Dad):
    def dear(self):
        print('from Son')
 
s = Son()
s.father()
 
#當某一個方法是私有的時候,子類就不能繼承父類的私有方法了
class Dad:
    def dear(self):
        print('from Dad')
    def __father(self):
        self.dear()
 
class Son(Dad):
    def dear(self):
        print('from Son')
 
s = Son()
s.father()

總之,在類中,靜態屬性,方法,物件屬性都可以變成私有的,只需要在這些名字之前加上__

私有的名字,在類內使用的時候,就是會變形成_該類名__方法名。下面這個例子:

以此為例 :沒有雙下換線會先找E中的func,但是有了雙下劃線,會在呼叫這個名字的類D中直接找_D__func

class D:
    def __init__(self):
        self.__func()
    def __func(self):
        print('in D')
 
class E(D):
    def __func(self):
        print('in E')
e = E()   #結果:<em id="__mceDel">in D</em><em id="__mceDel" style="font-size: 1.5em; background-color: #ffffff; font-family: "PingFang SC", "Helvetica Neue", Helvetica, Arial, sans-serif">  </em>

封裝與擴充套件性

封裝在於明確區分內外,使得類實現者可以修改封裝內的東西而不影響外部呼叫者的程式碼;而外部使用用者只知道一個介面(函式),只要介面(函式)名、引數不變,使用者的程式碼永遠無需改變。這就提供一個良好的合作基礎——或者說,只要介面這個基礎約定不變,則程式碼改變不足為慮。
#類的設計者,輕鬆的擴充套件了功能,而類的使用者完全不需要改變自己的程式碼

class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high
    def tell_area(self): #對外提供的介面,隱藏內部實現,此時我們想求的是體積,內部邏輯變了,<br>                           #只需求修該下列一行就可以很簡答的實現,而且外部呼叫感知不到,仍然使用該方法,但是功能已經變了
        return self.__width * self.__length * self.__high   #對於仍然在使用tell_area介面的人來說,根本無需改動自己的程式碼,就可以用上新功能