Python—面向對象 封裝03
阿新 • • 發佈:2018-07-29
bject 私有化 父類 setter 特殊 提升 ict 賬單 run
接著上面的一篇繼續往下:
如何隱藏
在python中用雙下劃線開頭的方式將屬性隱藏起來(設置成私有的)
class A: __x = 1 # _A__x = 1 def __init__(self, name): self.__name = name # self._A__name = name def __fun1(self): print("run fun1.......") def fun2(self): self.__fun1() # self._A__fun1() print("run fun2.......") a = A("Astro") # print(a.x) # 報錯 無法訪問到 x AttributeError: 'A' object has no attribute 'x' print(A.__dict__) # {'__module__': '__main__', '_A__x': 1, ........} # _A__x print(a._A__x) # 1 a.fun2() # run fun1....... # run fun2.......
?
其實這僅僅這是一種變形操作
類中所有雙下劃線開頭的名稱如__x都會自動變形成:_類名__x的形式:
A._A__N是可以訪問到的,即這種操作並不是嚴格意義上的限制外部訪問,僅僅只是一種語法意義上的變形
這種變形的特點:
- 在類外部無法直接obj.__AttrName
- 在類內部是可以直接使用:obj.__AttrName
- 在子類定義的__x不會覆蓋在父類定義的__x,因為子類中變形成了:_子類名__x,而父類中變形成了:_父類名__x,即雙下滑線開頭的屬性在繼承給子類時,子類是無法覆蓋的。
這種變形需要註意的問題是:
1、這種機制也並沒有真正意義上限制我們從外部直接訪問屬性,知道了類名和屬性名就可以拼出名字:_類名__屬性,然後就可以訪問了,如a._A__N
2、變形的過程只在類的定義時發生一次,在定義後的賦值操作,不會變形
a.__g = "gd"
print(a.__dict__)
# {'_A__name': 'Astro', '__g': 'gd'}
3、在繼承中,父類如果不想讓子類覆蓋自己的方法,可以將方法定義為私有的
# 正常情況 class A: def foo(self): print('A.foo') def bar(self): print('A.bar') self.foo() #b.foo() class B(A): def foo(self): print('B.foo') b=B() b.bar() # A.bar # B.foo
?
?
# 私有化後
class A:
def __foo(self): # #在定義時就變形為 _A__foo
print('A.foo')
def bar(self):
print('A.bar')
self.__foo() #self._A__foo() 只會與自己所在的類為準,即調用_A__fa
class B(A):
def __foo(self): #_B__foo
print('B.foo')
b=B()
b.bar()
# A.bar
# A.foo
?
?
?
封裝的意義
1.封裝數據
# 封裝數據屬性:明確的區分內外
class People:
def __init__(self, name, age):
self.__name = name
self.__age = age
def tell_info(self):
print("name:%s age:%s" % (self.__name, self.__age))
# 我們可以根據根據需要對 傳入的參數做限制
def set_info(self, name, age):
if not isinstance(name, str): # 判斷 傳進來的 name 是不是 str 類型
print("name must be str type")
return
if not isinstance(age, int):
print("age must be int type")
return
self.__name = name
self.__age = age
p = People("Astro", 15)
p.tell_info() # name:Astro age:15
# 通過調用 tell_info 這個接口,來間接訪問 name 和 age
p.set_info("茶水博士", 60)
p.tell_info() # name:茶水博士 age:60
p.set_info(1111,222) # name must be str type
?
?
2.封裝方法, 隔離復雜度
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()
# 插卡
# 用戶認證
# 輸入取款金額
# 打印賬單
# 取款
#----------------------------
#取款是功能,而這個功能有很多功能組成:插卡、密碼認證、輸入金額、打印賬單、取錢
#對使用者來說,只需要知道取款這個功能即可,其余功能我們都可以隱藏起來,很明顯這麽做
#隔離了復雜度,同時也提升了安全性
- 電視機本身是一個黑盒子,隱藏了所有細節,但是一定會對外提供了一堆按鈕,這些按鈕也正是接口的概念,所以說,封裝並不是單純意義的隱藏!!!
- 快門就是傻瓜相機為傻瓜們提供的方法,該方法將內部復雜的照相功能都隱藏起來了
?
?
?
3.特性(Property)
什麽是特性 property
property是一種特殊的屬性,訪問它時會執行一段功能(函數)然後返回值
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)
p = People("astro", 55, 1.73)
# print(p.bmi()) # 18.376825152861773
# 我們想得到 bmi 這個值,需要調用 p.bmi() ,如果在 def bmi(self) 上面加 @property ,即可直接調用了
print(p.bmi) # 18.376825152861773
p.height = 1.8
print(p.bmi) # 16.975308641975307
p.bmi = 20 # 報錯 AttributeError: can't set attribute
為什麽要用property
將一個類的函數定義成特性以後,對象再去使用的時候obj.name,根本無法察覺自己的name是執行了一個函數然後計算出來的,這種特性的使用方式遵循了統一訪問的原則
?
ps:面向對象的封裝有三種方式:
【public】
這種其實就是不封裝,是對外公開的
【protected】
這種封裝方式對外不公開,但對朋友(friend)或者子類公開
【private】
這種封裝對誰都不公開
?
python並沒有在語法上把它們三個內建到自己的class機制中,在C++裏一般會將所有的所有的數據都設置為私有的,然後提供set和get方法(接口)去設置和獲取,在python中通過property方法可以實現
class People:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name # obj.name訪問的是self.__NAME(這也是真實值的存放位置)
@name.setter
def name(self, val):
if not isinstance(val, str): # 在設定值之前進行類型檢查
print("姓名必須為字符串")
return
self.__name = val
@name.deleter
def name(self):
print("deleter....")
print("不允許刪除....")
p = People('astro')
print(p.name) # astro 此時可以直接訪問 name 屬性了
p.name = 'Astro'
print(p.name) # Astro name 別改變了,執行了 @name.setter 下的方法
p.name = 111 # 姓名必須為字符串
del p.name # deleter.... 不允許刪除....
Python—面向對象 封裝03