21.Python編程:訪問權(quán)限

前面一節(jié)重點(diǎn)學(xué)習(xí)了Python3中的面向?qū)ο笞罨A(chǔ)的類和對象的知識:類的定義、對象、構(gòu)造方法、屬性和方法等,在類中定義好的屬性,就可以賦值、取值等一系列操作了。

例如:定義一個SmartPhone類,源碼如下:

# 定義一個SmartPhone類
class SmartPhone(object):
    os = ""  # 系統(tǒng)
    type = ""  # 品牌
    memory = "4GB"  # 內(nèi)存大小 默認(rèn)單位GB

    # 構(gòu)造方法
    def __init__(self, os, type, memory):
        self.os = os
        self.type = type
        self.memory = memory

    # 定義一個打印智能手機(jī)屬性的方法
    def print_phone_info(self):
        print('os:', self.os, '\n品牌:', self.type, '\n內(nèi)存大小:', self.memory)

創(chuàng)建一個SmartPhone對象,打印調(diào)用打印信息方法print_phone_info()后,再修改屬性的值。但是,從前面SmartPhone類的定義來看,外部代碼還是可以自由地修改一個實(shí)例的type 、memory 、os等屬性的。如下:

# 創(chuàng)建一個SmartPhone類型的對象
phone1 = SmartPhone('Android', 'Galaxy', '6GB')
phone1.print_phone_info()

print('---------------------')

# 修改phone1指向的對象的屬性:品牌、內(nèi)存
phone1.type = "MeiZu 7Pro"
phone1.memory = '8GB'
phone1.print_phone_info()

運(yùn)行結(jié)果如下:

os: Android 
品牌: Galaxy 
內(nèi)存大小: 6GB
---------------------
os: Android 
品牌: MeiZu 7Pro 
內(nèi)存大小: 8GB

Java中是通過關(guān)鍵字:私有的private、公開的public、默認(rèn)受保護(hù)的protected等來限制屬性、方法的訪問權(quán)限的。如果在Python中,想要讓內(nèi)部屬性不被外部訪問,可以把屬性的名稱前加上兩個下劃線,即:__。

私有屬性

在Python中規(guī)定:實(shí)例的變量名如果以__開頭,就變成了一個私有變量(private),只有類內(nèi)部可以訪問,類外部不能訪問。

所以,我們把SmartPhone類的定義做一下修改:

# 定義一個SmartPhone類
class SmartPhone(object):
    __os = ""  # 私有屬性:系統(tǒng)
    __type = ""  # 私有屬性:品牌
    __memory = "4GB"  # 私有屬性:內(nèi)存大小 默認(rèn)單位GB

    # 構(gòu)造方法
    def __init__(self, os, type, memory):
        self.__os = os
        self.__type = type
        self.__memory = memory

    def print_phone_info(self):
        print('os:', self.__os, '\n品牌:', self.__type, '\n內(nèi)存大小:', self.__memory)

創(chuàng)建一個SmartPhone對象,打印調(diào)用打印信息方法print_phone_info()后,再訪問私有屬性,會報(bào)錯:AttributeError,如下:

# 創(chuàng)建一個SmartPhone類型的對象
phone1 = SmartPhone('Android', 'Galaxy', '6GB')
phone1.print_phone_info()

# 在類的外部訪問私有屬性__type,會報(bào)錯:AttributeError
phone1.__type

運(yùn)行結(jié)果:

Traceback (most recent call last):
os: Android 
  File "F:/python_projects/oop/23SmartPhone.py", line 21, in <module>
品牌: Galaxy 
    phone1.__type
內(nèi)存大小: 6GB
AttributeError: 'SmartPhone' object has no attribute '__type'

可以看到運(yùn)行結(jié)果報(bào)錯了,AttributeError: 'SmartPhone' object has no attribute '__type'意思是:屬性錯誤:SmartPhone的對象沒有__type屬性。

這是因?yàn)槲覀冊趖ype屬性前加了__,使__type變成了私有屬性,只有類內(nèi)部可以訪問,類外部不能訪問。

假如,我們在類的外部還想訪問、修改類里面的私有屬性怎么辦?可能你已經(jīng)想到了:給類添加訪問、修改私有屬性的方法!沒錯,修改如下:

# 定義一個SmartPhone類
class SmartPhone(object):
    __os = ""  # 私有屬性:系統(tǒng)
    __type = ""  # 私有屬性:品牌
    __memory = "4GB"  # 私有屬性:內(nèi)存大小 默認(rèn)單位GB

    # 構(gòu)造方法
    def __init__(self, os, type, memory):
        self.__os = os
        self.__type = type
        self.__memory = memory

    def print_phone_info(self):
        print('os:', self.__os, '\n品牌:', self.__type, '\n內(nèi)存大小:', self.__memory)

    #  對外界提供獲取__os的值的方法
    def get_os(self):
        return self.__os

    #  對外界提供設(shè)置__os的值的方法
    def set_os(self, os):
        self.__os = os

    #  對外界提供獲取__type的值的方法
    def get_type(self):
        return self.__type

    #  對外界提供設(shè)置__type的值的方法
    def set_type(self, types):
        self.__type = types

    #  對外界提供獲取__memory的值的方法
    def get_memory(self):
        return self.__memory

    #  對外界提供設(shè)置__memory的值的方法
    def set_memory(self, memory):
        self.__memory = memory

創(chuàng)建對象,在外界獲取和設(shè)置屬性的值如下:

# 創(chuàng)建一個SmartPhone類型的對象
phone1 = SmartPhone('Android', 'Galaxy', '6GB')
phone1.print_phone_info()

print('-------------------')

# 通過提供的set方法在類的外部訪問私有屬性
phone1.set_type("MeiZu 7Pro")
phone1.set_memory("8GB")

# 通過提供的get方法在類的外部訪問私有屬性
print('外部訪問:\n', phone1.get_os(), '\n', phone1.get_type(), '\n', phone1.get_memory())

運(yùn)行結(jié)果:

os: Android 
品牌: Galaxy 
內(nèi)存大小: 6GB
-------------------
外部訪問:
 Android 
 MeiZu 7Pro 
 8GB

可以看到運(yùn)行結(jié)果,在類的外部成功地獲取、修改私有屬性的值了。這樣做,不是自找麻煩嗎?那么,為什么要這樣多此一舉呢?這么做的意義非常大:在修改類中的屬性前,可以對外界傳入的數(shù)據(jù)做檢查。例如:外界傳入的品牌不是字符串,而是隨意傳入了一個整數(shù):

# 創(chuàng)建一個SmartPhone類型的對象
phone1 = SmartPhone('Android', 'Galaxy', '6GB')
phone1.print_phone_info()

print('-------------------')

phone1.set_type(2)
print(phone1.get_type())

運(yùn)行結(jié)果:

os: Android 
品牌: Galaxy 
內(nèi)存大小: 6GB
-------------------
2

這是未做檢查的運(yùn)行結(jié)果。在set_type()方法中做了數(shù)據(jù)檢查,其他代碼不變,更新的部分如下:

#  對外界提供設(shè)置__type的值的方法
    def set_type(self, types):
        if isinstance(types, str):
            self.__type = types
        else:
            raise ValueError(" SmartPhone type should be str!")

重新運(yùn)行,運(yùn)行結(jié)果:

os: Android 
Traceback (most recent call last):
品牌: Galaxy 
  File "F:/python_projects/oop/23SmartPhone.py", line 51, in <module>
內(nèi)存大小: 6GB
    phone1.set_type(2)
-------------------
  File "F:/python_projects/oop/23SmartPhone.py", line 33, in set_type
    raise ValueError(" SmartPhone type should be str!")

起到了對傳入的無效數(shù)據(jù)檢查的作用。關(guān)于Python中異常的處理后面會學(xué)習(xí),此處先拿來用下。raise:表示拋出異常,拋出一個ValueError的異常,并提示用戶:SmartPhone type should be str!

私有方法

Python中規(guī)定,私有方法同樣以兩個下劃線開頭,聲明該方法為私有方法,只能在類的內(nèi)部調(diào)用,不能在類地外部調(diào)用,形如:self.__method_name

在上面的SmartPhone類中添加一個私有方法__test(),代碼如下:

# 上接SmartPhone類,添加一個私有方法

    # 添加一個私有方法
    def __test(self):
        print("__test()是私有方法")

創(chuàng)建一個SmartPhone類型的對象,


# 創(chuàng)建一個SmartPhone類型的對象,并在外部調(diào)用該私有方法,
phone1 = SmartPhone('Android', 'Galaxy', '6GB')
phone1.print_phone_info()

print('-------------------')
# 在外部調(diào)用該私有方法,
phone1.__test()

運(yùn)行結(jié)果:

os: Android 
Traceback (most recent call last):
品牌: Galaxy 
  File "F:/python_projects/oop/23SmartPhone.py", line 55, in <module>
內(nèi)存大小: 6GB
    phone1.__test()
-------------------
AttributeError: 'SmartPhone' object has no attribute '__test'

報(bào)錯AttributeError: 'SmartPhone' object has no attribute '__test',意思是:屬性錯誤:SmartPhone的對象沒有__test屬性。

提示

需要特別注意的是,在Python中,變量名類似__xxxx__的,也就是以雙下劃線開頭,并且以雙下劃線結(jié)尾的,是特殊變量。而特殊變量是可以直接訪問的,并不是私有的變量,所以我們自己在定義類中的私有屬性時,千萬不能用__xxxx__這樣的變量名,以免混淆。

有些時候,你會看到以一個下劃線開頭的實(shí)例變量名,比如_xxxx,這樣的實(shí)例變量外部也是可以訪問的。但是,按照Python約定俗成的規(guī)定,好的習(xí)慣是,當(dāng)你看到這樣的變量時,意思就是,“雖然我可以被訪問,但是,請把我視為私有變量,不要隨意訪問”。

注意:
雙下劃線開頭的實(shí)例變量是不是一定不能從外部訪問呢?其實(shí)也不是。

上面例子中,不能直接訪問__type是因?yàn)镻ython解釋器對外把__type變量改成了_SmartPhone__type,所以,仍然可以通過_SmartPhone__type來訪問__type變量。例如:

# 創(chuàng)建一個SmartPhone類型的對象
phone1 = SmartPhone('Android', 'Galaxy', '6GB')
phone1.print_phone_info()

print('-------------------')

print(phone1._SmartPhone__os)
print(phone1._SmartPhone__type)
print(phone1._SmartPhone__memory)

運(yùn)行結(jié)果:

os: Android 
品牌: Galaxy 
內(nèi)存大小: 6GB
-------------------
Android
Galaxy
6GB

但是強(qiáng)烈建議你不要這么干,因?yàn)椴煌姹镜腜ython解釋器可能會把__xxxx改成不同的變量名,不一定總是_類名__xxxx。

易錯點(diǎn)舉例

# 上接SmartPhone類定義

# 創(chuàng)建一個SmartPhone類型的對象
phone1 = SmartPhone('Android', 'Galaxy', '6GB')
phone1.print_phone_info()

print('-------------------')

# 修改phone1指向的對象的內(nèi)存為"8GB"
phone1.__memory = "8GB"
print('__memory :', phone1.__memory)

print('get_memory() :', phone1.get_memory())
print('_SmartPhone__memory :', phone1._SmartPhone__memory)

從表面上看,我們在外部代碼中“成功”地把"8GB"設(shè)置了__memory 變量,但實(shí)際上這個__memory變量和class內(nèi)部的__memory變量不是一個變量!內(nèi)部的__memory變量已經(jīng)被Python解釋器自動改成了_SmartPhone__memory,而外部代碼給phone1新增了一個__memory變量。

運(yùn)行結(jié)果:

os: Android 
品牌: Galaxy 
內(nèi)存大小: 6GB
-------------------
__memory : 8GB
get_memory() : 6GB
_SmartPhone__memory : 6GB

運(yùn)行結(jié)果印證了上面的話,這是一個易忽略點(diǎn),要留意。

說明

類中還預(yù)定義了一些方法,Python的class中還有許多這樣有特殊用途的函數(shù),可以幫助我們定制類,我們會在以后一一詳細(xì)學(xué)習(xí)。

類的專有方法:
__init__ : 構(gòu)造函數(shù),在生成對象時調(diào)用
__del__ : 析構(gòu)函數(shù),釋放對象時使用
__repr__ : 打印,轉(zhuǎn)換
__setitem__ : 按照索引賦值
__getitem__: 按照索引獲取值
__len__: 獲得長度
__cmp__: 比較運(yùn)算
__call__: 函數(shù)調(diào)用
__add__: 加運(yùn)算
__sub__: 減運(yùn)算
__mul__: 乘運(yùn)算
__div__: 除運(yùn)算
__mod__: 求余運(yùn)算
__pow__: 乘方

小結(jié)

本文主要通過幾個簡單的例子來學(xué)習(xí)類中私有屬性和方法,以及它們之間的區(qū)別,要熟練掌握。

如果你觀察的足夠仔細(xì),你會發(fā)現(xiàn),例子中我們屬性只有3個,增加了3對get\set方法,也就是6個。那么,如果屬性非常多時,我們上面的做法又會顯得非常麻煩,這有沒有好的解決方案呢?答案是有的。python中給我們提供了一些關(guān)鍵詞@property,專門幫我們解決上面的問題,我們會在后面詳細(xì)學(xué)習(xí)這些知識。

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

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

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