python基礎----封裝、封裝與擴展性

   從封裝自己的意思去理解,封裝就好像是拿來一個麻袋,把小貓,小狗,小王八,還有egon和alex一塊兒裝進麻袋,而後把麻袋封上口子。但其實這種理解至關片面python

首先咱們要了解編程

要封裝什麼

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

你的性取向(數據的封裝)編程語言

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

爲何要封裝

封裝數據的主要緣由是:保護隱私(做爲男人的你,臉上就寫着:我喜歡男人,你懼怕麼?)函數

封裝方法的主要緣由是:隔離複雜度(快門就是傻瓜相機爲傻瓜們提供的方法,該方法將內部複雜的照相功能都隱藏起來了,好比你沒必要知道你本身的尿是怎麼流出來的,你直接掏出本身的接口就能用尿這個功能)spa

你的身體沒有一處不體現着封裝的概念:你的身體把膀胱尿道等等這些尿的功能隱藏了起來,而後爲你提供一個尿的接口就能夠了(接口就是你的。。。,),你總不能把膀胱掛在身體外面,上廁所的時候就跟別人炫耀:hi,man,你瞅個人膀胱,看看我是怎麼尿的。還有你的頭把你的腦子封裝到了腦袋裏,而後提供了眼睛這個接口....設計

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

封裝分爲兩個層面

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

第一個層面的封裝(什麼都不用作):建立類和對象會分別建立兩者的名稱空間,咱們只能用類名.或者obj.的方式去訪問裏面的名字,這自己就是一種封裝

>>> r1.nickname
'草叢倫'
>>> Riven.camp
'Noxus'

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

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

在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()
>>> a._A__N
>>> a._A__X
>>> A._A__N

2.變形的過程只在類的定義是發生一次,在定義後的賦值操做,不會變形

3.在繼承中,父類若是不想讓子類覆蓋本身的方法,能夠將方法定義爲私有的

#正常狀況
>>> 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並不會真的阻止你訪問私有的屬性,模塊也遵循這種約定,若是模塊名以單下劃線開頭,那麼from module import *時不能被導入,可是你from module import _private_module依然是能夠導入的

其實不少時候你去調用一個模塊的功能時會遇到單下劃線開頭的(socket._socket,sys._home,sys._clear_type_cache),這些都是私有的,原則上是供內部調用的,做爲外部的你,獨斷獨行也是能夠用的,只不過顯得稍微傻逼一點點

python要想與其餘編程語言同樣,嚴格控制屬性的訪問權限,只能藉助內置方法如__getattr__,詳見面向對象進階.

 

課堂筆記:

class A:
    __x=1    #_A__x
    def __test(self:   #_A__test
        print('from A')

print(A.__x)
print(A._A__x)
a=A()
print(a._A__x)
print(A.__dict__)
print(A.__dict__)
A._A__test(123)

a=A()
a._A__test()




# __名字,這種語法,只在定義的時候纔會有變形的效果,若是類或者對象已經產生了,就不會有變形效果

class B:
    pass

B.__x=1
print(B.__dict__)
print(B.__x)

b=B()
b.__x=1
print(b.__dict__)
print(b.__x)




#在定義階段就會變形
class A:
    def __fa(self):     #_A__fa
        print('from A')
    def test(self):
        self.__fa()      #self._A__fa

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




class A:
    def __init__(self):
        self.__x=1
    def tell(self):
        print(self.__x)          # self.__x    在類內部就能夠直接用__名字來訪問到變形的屬性

a=A()
print(a.__dict__)
print(a.__x)
a.tell()
筆記

 

封裝與擴展性

封裝在於明確區份內外,使得類實現者能夠修改封裝內的東西而不影響外部調用者的代碼;而外部使用用者只知道一個接口(函數),只要接口(函數)名、參數不變,使用者的代碼永遠無需改變。這就提供一個良好的合做基礎——或者說,只要接口這個基礎約定不變,則代碼改變不足爲慮。

#類的設計者
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): #對外提供的接口,隱藏了內部的實現細節,此時咱們想求的是面積
        return self.__width * self.__length
#使用者
>>> r1=Room('臥室','egon',20,20,20)
>>> r1.tell_area() #使用者調用接口tell_area
400
#類的設計者,輕鬆的擴展了功能,而類的使用者徹底不須要改變本身的代碼
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): #對外提供的接口,隱藏內部實現,此時咱們想求的是體積,內部邏輯變了,只需求修該下列一行就能夠很簡答的實現,並且外部調用感知不到,仍然使用該方法,可是功能已經變了
        return self.__width * self.__length * self.__high

對於仍然在使用tell_area接口的人來講,根本無需改動本身的代碼,就能夠用上新功能

>>> r1.tell_area()
8000
相關文章
相關標籤/搜索