封裝與特性(property)
封裝
封裝就是把一些事物進行隱藏到一個不為人知道的地方
隱藏
在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的形式訪問到. #A._A__N是可以訪問到的,即這種操作並不是嚴格意義上的限制外部訪問,僅僅只是一種語法意義上的變形
這種自動變形的特點:
- 類中定義的__x只能在內部使用,如self.__x,引用的就是變形的結果。
- 這種變形其實正是針對外部的變形,在外部是無法通過__x這個名字訪問到的。
- 在子類定義的__x不會覆蓋在父類定義的__x,因為子類中變形成了:_子類名__x,而父類中變形成了:_父類名__x,即雙下滑線開頭的屬性在繼承給子類時,子類是無法覆蓋的。
需註意的問題:
1、這種機制也並沒有真正意義上限制我們從外部直接訪問屬性,知道了類名和屬性名就可以拼出名字:_類名__屬性,然後就可以訪問了,如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
封裝的目的
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:封裝方法:目的是隔離復雜度
特性(property)
property是一種特殊的屬性,訪問它時會執行一段功能(函數)然後返回值
為什麽要用property
將一個類的函數定義成特性以後,對象再去使用的時候obj.name,根本無法察覺自己的name是執行了一個函數然後計算出來的,這種特性的使用方式遵循了統一訪問的原則
例一:BMI指數(bmi是計算而來的,但很明顯它聽起來像是一個屬性而非方法,如果我們將其做成一個屬性,更便於理解)
成人的BMI數值:
過輕:低於18.5
正常:18.5-23.9
過重:24-27
肥胖:28-32
非常肥胖, 高於32
體質指數(BMI)=體重(kg)÷身高^2(m)
EX:70kg÷(1.75×1.75)=22.86
class People:
def __init__(self,name,weight,height):
self.name=name
self.weight=weight
self.height=height
@property
def bmi(self):
return self.weight / (self.height**2)
p1=People(‘egon‘,75,1.85)
print(p1.bmi)
例二:圓的周長和面積
import math
class Circle:
def __init__(self,radius): #圓的半徑radius
self.radius=radius
@property
def area(self):
return math.pi * self.radius**2 #計算面積
@property
def perimeter(self):
return 2*math.pi*self.radius #計算周長
c=Circle(10)
print(c.radius)
print(c.area) #可以向訪問數據屬性一樣去訪問area,會觸發一個函數的執行,動態計算出一個值
print(c.perimeter) #同上
‘‘‘
輸出結果:
314.1592653589793
62.83185307179586
‘‘‘
註意:此時的特性area和perimeter不能被賦值
封裝與擴展性
封裝在於明確區分內外,使得類實現者可以修改封裝內的東西而不影響外部調用者的代碼;而外部使用用者只知道一個接口(函數),只要接口(函數)名、參數不變,使用者的代碼永遠無需改變。這就提供一個良好的合作基礎——或者說,只要接口這個基礎約定不變,則代碼改變不足為慮
#類的設計者
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
#類的設計者,輕松的擴展了功能,而類的使用者完全不需要改變自己的代碼
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()
封裝與特性(property)