類的封裝


從封裝本身的意思去理解,封裝就好像是拿來一個麻袋,把小貓,小狗,小王八,還有egon一起裝進麻袋,然后把麻袋封上口子。但其實這種理解相當片面

一、封裝什么

  • 你錢包的有多少錢(數據的封裝)

  • 你的性取向(數據的封裝)

  • 你撒尿的具體功能是怎么實現的(方法的封裝)

二、為什么要封裝

封裝數據的主要原因是:保護隱私(作為男人的你,臉上就寫着:我喜歡男人,你害怕么?)

封裝方法的主要原因是:隔離復雜度(快門就是傻瓜相機為傻瓜們提供的方法,該方法將內部復雜的照相功能都隱藏起來了,比如你不必知道你自己的尿是怎么流出來的,你直接掏出自己的接口就能用尿這個功能)

提示:在編程語言里,對外提供的接口(接口可理解為了一個入口),就是函數,稱為接口函數,這與接口的概念還不一樣,接口代表一組接口函數的集合體。

三、兩個層面的封裝

封裝其實分為兩個層面,但無論哪種層面的封裝,都要對外界提供好訪問你內部隱藏內容的接口(接口可以理解為入口,有了這個入口,使用者無需且不能夠直接訪問到內部隱藏的細節,只能走接口,並且我們可以在接口的實現上附加更多的處理邏輯,從而嚴格控制使用者的訪問)

3.1 第一個層面

第一個層面的封裝(什么都不用做):創建類和對象會分別創建二者的名稱空間,我們只能用類名.或者obj.的方式去訪問里面的名字,這本身就是一種封裝

注意:對於這一層面的封裝(隱藏),類名.和實例名.就是訪問隱藏屬性的接口

3.2 第二個層面

第二個層面的封裝:類中把某些屬性和方法隱藏起來(或者說定義成私有的),只在類的內部使用、外部無法訪問,或者留下少量接口(函數)供外部訪問。

在python中用雙下划線的方式實現隱藏屬性(設置成私有的)

類中所有雙下划線開頭的名稱如__x都會自動變形成:_類名__x的形式:

class A:
    __N = 0  # 類的數據屬性就應該是共享的,但是語法上是可以把類的數據屬性設置成私有的如__N,會變形為_A__N

    def __init__(self):
        self.__X = 10  # 變形為self._A__X

    def __foo(self):  # 變形為_A__foo
        print('from A')

    def bar(self):
        self.__foo()  # 只有在類內部才可以通過__foo的形式訪問到.

這種自動變形的特點:

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

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

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

注意:對於這一層面的封裝(隱藏),我們需要在類中定義一個函數(接口函數)在它內部訪問被隱藏的屬性,然后外部就可以使用了

這種變形需要注意的問題是:

  1. 這種機制也並沒有真正意義上限制我們從外部直接訪問屬性,知道了類名和屬性名就可以拼出名字:_類名__屬性,然后就可以訪問了,如a._A__N
# 對象測試
a = A()
print(a._A__N)
0
# 對象測試
print(a._A__X)
10
# 類測試
print(A._A__N)
0
# 類測試
try:
    print(A._A__X)  # 對象私有的屬性
except Exception as e:
    print(e)
type object 'A' has no attribute '_A__X'
  1. 變形的過程只在類的定義時發生一次,在定義后的賦值操作,不會變形
a = A()
print(a.__dict__)
{'_A__X': 10}
a.__Y = 1
print(a.__dict__)
{'_A__X': 10, '__Y': 1}
  1. 在繼承中,父類如果不想讓子類覆蓋自己的方法,可以將方法定義為私有的
# 正常情況
class A:
    def fa(self):
        print('from A')

    def test(self):
        self.fa()


class B(A):
    def fa(self):
        print('from B')


b = B()
b.test()
from B
# 把fa定義成私有的,即__fa
class A:
    def __fa(self):  # 在定義時就變形為_A__fa
        print('from A')

    def test(self):
        self.__fa()  # 只會與自己所在的類為准,即調用_A__fa


class B(A):
    def __fa(self):
        print('from B')


b = B()
b.test()
from A

四、私有模塊

python並不會真的阻止你訪問私有的屬性,模塊也遵循這種約定,如果模塊中的變量名_private_module以單下划線開頭,那么from module import *時不能被導入該變量,但是你from module import _private_module依然是可以導入該變量的

其實很多時候你去調用一個模塊的功能時會遇到單下划線開頭的(socket._socket,sys._home,sys._clear_type_cache),這些都是私有的,原則上是供內部調用的,作為外部的你,一意孤行也是可以用的,只不過顯得稍微傻逼一點點

python要想與其他編程語言一樣,嚴格控制屬性的訪問權限,只能借助內置方法如__getattr__,詳見面向對象高級部分。

五、練習

  • 多態是在定義角度

  • 多態性是在調用角度(使用角度)

class A:
    def fa(self):
        print('from A')

    def test(self):
        self.fa()


class B(A):
    def fa(self):
        print('from B')


b = B()
b.test()
from B
class A:
    def __fa(self):
        print('from A')

    def test(self):
        self.__fa()


class B(A):
    def __fa(self):
        print('from B')


b = B()
b.test()
from A

注:__名字,這種語法只在定義的時候才有變形的效果,如果類或對象已經產生了,就不會有變形的效果了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM