1、為什么要封裝
封裝不是單純意義的隱藏
1:封裝數據:主要原因是:保護私隱,明確區分內外。將數據隱藏起來這不是目的。隱藏起來然后對外提供操作該數據的接口,然后我們可以在接口附加上對該數據操作的限制,以此完成對數據屬性操作的嚴格控制。

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()
2:封裝方法:目的是隔離復雜度
封裝方法舉例:
1. 你的身體沒有一處不體現着封裝的概念:你的身體把膀胱尿道等等這些尿的功能隱藏了起來,然后為你提供一個尿的接口就可以了(接口就是你的。。。,),你總不能把膀胱掛在身體外面,上廁所的時候就跟別人炫耀:hi,man,你瞅我的膀胱,看看我是怎么尿的。
2. 電視機本身是一個黑盒子,隱藏了所有細節,但是一定會對外提供了一堆按鈕,這些按鈕也正是接口的概念,所以說,封裝並不是單純意義的隱藏!!!
3. 快門就是傻瓜相機為傻瓜們提供的方法,該方法將內部復雜的照相功能都隱藏起來了
提示:在編程語言里,對外提供的接口(接口可理解為了一個入口),可以是函數,稱為接口函數,這與接口的概念還不一樣,接口代表一組接口函數的集合體。

#取款是功能,而這個功能有很多功能組成:插卡、密碼認證、輸入金額、打印賬單、取錢 #對使用者來說,只需要知道取款這個功能即可,其余功能我們都可以隱藏起來,很明顯這么做隔離了復雜度,同時也提升了安全性 class ATM: def __card(self): print('插卡') def __auth(self): print('用戶認證') def __input(self): print('輸入取款金額') def __print_bill(self): print('打印賬單') def __take_money(self): print('取款') def withdraw(self): self.__card() self.__auth() self.__input() self.__print_bill() self.__take_money() a=ATM() a.withdraw()
2、封裝例子
__名字,這種語法,只在定義的時候才會有變形的效果,如果類或者對象已經產生了,就不會有變形效果

classA: __x=1 #在屬性前面加兩個下划線,表示對該屬性進行隱藏,設置成私有,在內部都會變成成:_類名.__x def__test(self): #這里在內部會變形:_A__test,調用的時候a._A__test() print('fromA') def__init__(self): self.__x=10 #變形為self._A__x def__foo(self): #變形為_A.__foo print('fromA') def bar(self): self.__foo() #只有在類內部才可以通過__foo的形式訪問 #這就是封裝,簡單的隱藏 #a=A() #print(a._A__x) #a._A__test()#不建議在外部直接通過這種方式調用隱藏方法
這種自動變形的特點:
1.類中定義的__x只能在內部使用,如self.__x,引用的就是變形的結果。
2.這種變形其實正是針對外部的變形,在外部是無法通過__x這個名字訪問到的。
3.在子類定義的__x不會覆蓋在父類定義的__x,因為子類中變形成了:_子類名__x,而父類中變形成了:_父類名__x,即雙下滑線開頭的屬性在繼承給子類時,子類是無法覆蓋的。
這種變形需要注意的問題是:
1.這種機制也並沒有真正意義上限制我們從外部直接訪問屬性,知道了類名和屬性名就可以拼出名字:_類名__屬性,然后就可以訪問了,如a._A__N
2.變形的過程只在類的定義是發生一次,在定義后的賦值操作,不會變形

class A: def fa(self): print('from A_fa') def test(self): self.fa() # b.fa class B(A): def fa(self): print('from B_fa') pass b=B() b.test() # b.test --- >B---->A----> b.fa()----> b 是 B 的對象,在 B 里找 fa

class A: def __init__(self): self.__x=1 # 對象調用的時候 self._A__x 或者定義一個接口 def tell(self): # 定義統一接口,對象去調用這個 tell() 接口查看 __x print(self.__x) # 在類的內部,可以直接這樣調用 __x,外部是 _A__x # 法1:不推薦 a=A() print(a._A__x) # 法2: 可以用 a.tell()

# 知識點:在繼承中,父類如果不想讓子類覆蓋自己的方法,可以將方法定義為私有的 # __名字,在定義節點就已經變形了,變成 _類名__屬性 class A: def __fa(self): # 變形:_A__fa,fa 變成私有 print('from A_fa') def test(self): self.__fa() # 在定義的時候就變形了,self.__fa() 變成:self._A__fa() class B(A): def __fa(self): # 變形:_B__fa print('from B_fa') pass b=B() b.test() # b.test --- >B沒有---->找A的test----> b.fa()----> b._A__fa 找的A的 __fa
3、封裝特性
什么是特性property
property是一種特殊的屬性,訪問它時會執行一段功能(函數)然后返回值
為什么要用property
將一個類的函數定義成特性以后,對象再去使用的時候obj.name,根本無法察覺自己的name是執行了一個函數然后計算出來的,這種特性的使用方式遵循了統一訪問的原則

import math class Circle: # 圓周率 def __init__(self,radius): # 圓的半徑 self.radius=radius @property # area=property(area) def area(self): return math.pi * self.radius**2 # 計算圓的面積 @property def perimeter(self): return 2*math.pi*self.radius # 計算周長 c=Circle(7) print(c.area) # 偽裝成數據屬性,如果不加 property 的話,調用的時候變成 c.area(),一個函數屬性 property 簡單來說就把類里的函數屬性偽裝成一個數據屬性,使用者用起來感覺不到自己用的其實一個函數 #注意:此時的特性arear和perimeter不能被賦值,因為實質上是一個函數屬性 c.area=3 #為特性area賦值 ''' 拋出異常: AttributeError: can't set attribute '''
ps:面向對象的封裝有三種方式:
【public】
這種其實就是不封裝,是對外公開的
【protected】
這種封裝方式對外不公開,但對朋友(friend)或者子類(形象的說法是“兒子”,但我不知道為什么大家不說“女兒”,就像“parent”本來是“父母”的意思,但中文都是叫“父類”)公開
【private】
這種封裝對誰都不公開

class people: def __init__(self,name,age,height,weight): self.name=name self.age=age self.height=height self.weight=weight @property def bodyindex(self): return self.weight/(self.height**2) p1=people('alex',18,1.76,74) print(p1.bodyindex)

class people: def __init__(self,name,SEX): self.name=name # self.__sex=SEX # 性別隱藏起來不讓人知道 self.sex=SEX # # p2.sex=male 一初始化或賦值操作就找 sex.setter @property # 負責查詢 def sex(self): # 通過接口可以查看隱藏的性別 return self.__sex # p2.__sex= male @sex.setter # 定義修改性別的接口 def sex(self,value): # sexes=['male','female'] if not isinstance(value,str): # 在設定值之前進行類型檢查,增加限制的擴展性 raise TypeError('性別必須是字符串類型') # if value not in sexes: # raise TypeError('性別只能是 male 或者 female') self.__sex=value # p2.__sex= male @sex.deleter def sex(self): # 刪除屬性接口 del self.__sex # del p2.__sex p2=people('alex','male') # 觸發 init 執行,這里有個賦值操作 p2.sex='male' print(p2.sex) # p2.sex='female3' # print(p2.sex) del p2.sex # 刪掉 __sex 數據屬性 print(p2.sex) # 再去 property 找的話找不到了
被 property 裝飾的屬性會優先於對象的屬性被使用,被找到
而被 property裝飾的屬性,如 sex ,分成三種
property 查詢
sex.setter 賦值,修改
sex.deleter 刪除
如果對象要修改數據屬性的時候,在沒有 property 的情況下,可以隨便改,但是加了之后,就可以有一個擴展性,限制對象只能改什么