02類與對(duì)象:封裝,屬性方法隱藏和property裝飾器

封裝

0 引入

面向?qū)ο缶幊逃腥筇匦裕悍庋b、繼承、多態(tài),其中最重要的一個(gè)特性就是封裝。封裝指的就是把數(shù)據(jù)與功能都整合到一起,聽起來是不是很熟悉,沒錯(cuò),我們之前所說的”整合“二字其實(shí)就是封裝的通俗說法。除此之外,針對(duì)封裝到對(duì)象或者類中的屬性,我們還可以嚴(yán)格控制對(duì)它們的訪問,分兩步實(shí)現(xiàn):隱藏與開放接口

一、隱藏類屬性和方法

如何隱藏

  • 在變量前加__
# 如何隱藏呢? 類屬性和 方法前面加__。對(duì)外不對(duì)內(nèi)

class Foo:
    __x =1

    def __f1(self):
        print("__f1內(nèi)部可以訪問")

    def f2(self):
        self.__f1()
        print(self.__x)

f = Foo()
#f.x 不能訪問__x
print(Foo.__dict__)

"""{'__module__': '__main__', '_Foo__x': 1, 
'_Foo__f1': <function Foo.__f1 at 0x0000000002784310>, 
'__dict__': <attribute '__dict__' of 'Foo' objects>, 
'__weakref__': <attribute '__weakref__' of 'Foo' objects>,
'__doc__': None}
"""
# 從上面可以看出外部訪問可以通過_Foo__x

print(Foo._Foo__x)  # 1
print(Foo._Foo__f1)  # <function Foo.__f1 at 0x0000000002784310>
# __對(duì)外不對(duì)內(nèi),內(nèi)部可以直接訪問__,如下:
Foo().f2()
"""
__f1內(nèi)部可以訪問
1
"""

隱藏屬性

設(shè)計(jì)者: 阿登

class People:

    def __init__(self, name):
        self.__name = name

    def get_name(self):
        # 通過接口就可以間接地訪問到名字屬性
        print(self.__name)
    
    def change_name(self, name):
        if not isinstance(name, str):
            print("必須傳字符串")
            raise TypeError("必須傳字符串")
        self.__name = name
        print(f"修改后名字為:{self.__name}")
        

 使用者:向佳

p = People("咸維維")

那么不能直接訪問__name,我可以開放一個(gè)接口訪問

p.get_name() # 這樣就能訪問到name了

再比如我想要修改__name的屬性,作為設(shè)計(jì)者我給你開放一個(gè)接口讓你修改
作為設(shè)計(jì)者_(dá)_name的設(shè)計(jì)我并不想讓使用者直接修改 刪除等操作。那么
作為設(shè)計(jì)者我給你開放一個(gè)接口供你訪問和修改屬性。
p.change_name("阿登")

隱藏函數(shù)屬性

目的的是為了隔離復(fù)雜度,當(dāng)使用調(diào)用的時(shí)候點(diǎn)方法的時(shí)候只需要使用想調(diào)的方法即可,無需在多個(gè)方法中選擇,例如ATM程序的取款功能,該功能有很多其他功能組成,比如插卡、身份認(rèn)證、輸入金額、打印小票、取錢等,而對(duì)使用者來說,只需要開發(fā)取款這個(gè)功能接口即可,其余功能我們都可以隱藏起來

>>> class ATM:
...     def __card(self): #插卡
...         print('插卡')
...     def __auth(self): #身份認(rèn)證
...         print('用戶認(rèn)證')
...     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()
...
>>> obj=ATM()
>>> obj.withdraw()

為什么要隱藏

總結(jié)隱藏屬性與開放接口,本質(zhì)就是為了明確地區(qū)分內(nèi)外,類內(nèi)部可以修改封裝內(nèi)的東西而不影響外部調(diào)用者的代碼;而類外部只需拿到一個(gè)接口,只要接口名、參數(shù)不變,則無論設(shè)計(jì)者如何改變內(nèi)部實(shí)現(xiàn)代碼,使用者均無需改變代碼。這就提供一個(gè)良好的合作基礎(chǔ),只要接口這個(gè)基礎(chǔ)約定不變,則代碼的修改不足為慮。

property裝飾器

裝飾器是在不修改被裝飾器對(duì)象源碼代碼以及調(diào)用方式的前提下為被裝飾對(duì)象添加新功能的可調(diào)用對(duì)象

例一:BMI指數(shù)(bmi是計(jì)算而來的,但很明顯它聽起來像是一個(gè)屬性而非方法,如果我們將其做成一個(gè)屬性,更便于理解)

成人的BMI數(shù)值:
過輕:低于18.5
正常:18.5-23.9
過重:24-27
肥胖:28-32
非常肥胖, 高于32
  體質(zhì)指數(shù)(BMI)=體重(kg)÷身高^2(m)
  EX:70kg÷(1.75×1.75)=22.86

身高或體重是不斷變化的,因而每次想查看BMI值都需要通過計(jì)算才能得到,但很明顯BMI聽起來更像是一個(gè)特征而非功能,
為此Python專門提供了一個(gè)裝飾器property,可以將類中的函數(shù)“偽裝成”對(duì)象的數(shù)據(jù)屬性,
對(duì)象在訪問該特殊屬性時(shí)會(huì)觸發(fā)功能的執(zhí)行,然后將返回值作為本次訪問的結(jié)果,例如

property是一個(gè)裝飾器,是用來綁定給對(duì)象的方法偽裝成一個(gè)數(shù)據(jù)屬性,就可以不用加括號(hào)調(diào)用

class People:
    def __init__(self,name,weight,height):
        self.name=name
        self.weight=weight
        self.height=height

    # bmi聽起來更像是1個(gè)數(shù)據(jù)屬性,而非功能

    @property
    def bmi(self):
        return self.weight / (self.height**2)

p1=People('adneg',75,1.80)
print(p1.bmi) # 23.148148148148145

p1.weight = 70
print(p1.bmi)  # 21.604938271604937

使用property有效地保證了屬性訪問的一致性。另外property還提供設(shè)置和刪除屬性的功能,如下

class People1:

    def __init__(self, name):
        self.__name = name


    def get_name(self):
        # 通過接口就可以間接地訪問到名字屬性
        return self.__name

    def change_name(self, name):
        if not isinstance(name, str):
            print("必須傳字符串")
            raise TypeError("必須傳字符串")
        self.__name = name
        print(f"修改后名字為:{self.__name}")

    def del_name(self):
        print("不讓刪")
        # del self.__name

    name = property(get_name, change_name, del_name)

obj1 = People1("阿登")
"""
1.  屬性隱藏起來
2. 開放接口
3.  name = property(get_name, change_name, del_name)
"""
# obj1.get_name()
# obj1.change_name("向佳")
# obj1.del_name()

print(obj1.name)
del obj1.name
obj1.name = "王潤(rùn)"

name = property(get_name, change_name, del_name)

上面那種方式是老版本的使用方式下面用另外一種更直觀的方式實(shí)現(xiàn):

如果有個(gè)關(guān)鍵屬性你想讓外部使用者的操作嚴(yán)格控制起來,步驟:
1. 隱藏起來
2. 開放三個(gè)接口 查看 修改 刪除 三個(gè)接口,三個(gè)接口的命名一直,同需要操作的名字一樣

3.三個(gè)接口操作偽裝成一個(gè)數(shù)據(jù)屬性 查找@property 修改 @name.setter 刪除 @name.deleter name就是你想操作的屬性name,如果編程name123那么你三個(gè)接口的方法名也需要編程name123 查找@property 修改 @name123.setter 刪除 @name123.deleter

class People2:

    def __init__(self, name):
        self.__name = name

    @property    # name = property(name)
    def name(self):  # p2.name
        # 通過接口就可以間接地訪問到名字屬性
        return self.__name

    @name.setter
    def name(self, name): # p2.name = "老胡"
        if not isinstance(name, str):
            print("必須傳字符串")
            raise TypeError("必須傳字符串")
        self.__name = name
        print(f"修改后名字為:{self.__name}")

    @name.deleter
    def name(self): # del p2.name
        print("不讓刪")
        # del self.__name


p2 = People2("毛主席")
print(p2.name)  # 毛主席
p2.name = "老胡" # 修改后名字為:老胡
del p2.name  # 不讓刪

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容