首頁 > 軟體

python中的property及屬性與特性之間的優先權

2022-07-15 18:00:41

 前言

這幾天看《流暢的python》這本書了,在一個範例中又看到了property作為裝飾器在使用,因為很久沒有用這個東西了,對它的一些特性和使用方法等都不是很熟悉,所以又專門在搜了幾篇部落格和在官方檔案中學習了它的相關用法。再者又剛好學到了python中的屬性(attribute),所以剛好這兩者可以兩相對比,也許更好理解!

屬性(attribute)

屬性的定義

在python中,屬性其實是(物件的)屬性和(物件的)方法的集合。

一開始我以為就是:屬性就是屬性,方法就是方法,一個是“屬性”,一個是行為,但是在開啟IDE寫了一個測試案例後,我就信服了:

# python中資料的屬性和處理資料的方法統稱為屬性
class Cat:

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

    def run(self):
        print('小碎步...')

if __name__ == '__main__':
    tom = Cat('Tom')
    print(tom.__getattribute__('name'))  # 物件的屬性
    print(tom.__getattribute__('run'))  # 物件的方法

在這裡我定義了一個類,同時在初始化方法中為它新增了一個屬性name,然後定義了一個run方法,之後在main方法裡建立了tom這個範例,使用__getattribute__()方法來獲取它的name屬性和run屬性,果不其然,居然真的都獲取到了!

 所以,以後再也不要說屬性只是單純的“屬性”了!

屬性的用法

屬性用法很簡單,不管是物件的屬性還是方法,都是用.來獲取或者呼叫。

1.設定或修改物件的屬性

tom.name='Tom' #設定(修改)物件tom的name屬性值為Tom

2.刪除物件的屬性

del tom.name #使用del關鍵字刪除物件的屬性

 可以看到,屬性刪除後就不能再獲取了。

3.呼叫物件的方法(屬性)

tom.run() #直接用.呼叫即可

4.物件的方法置None

物件的方法我們無法刪除,但是我們可以將其置為None。

tom.run=None

特性(property)

特性的定義

個人理解:特性就是屬性的特例,因為預先知道某些屬性的一些特定條件,比如取值範圍,型別等等,所以在對這些屬性進行操作的時候,為了在操作前後,保持一些特定條件不變(就是不準越界操作),所以就有了特性來約束它。

來看一下官方檔案給的例子:

官方檔案中提到:一個典型的例子就是託管x(這裡的x是一個類的屬性), 使用方法也特別的直接明瞭:如果 c 為 C 的範例,c.x 將呼叫 getter,c.x = value 將呼叫 setter, del c.x 將呼叫 deleter。就是在對c.x這個屬性進行操作時,可以通過getter,setter,delx來控制它的修改和刪除。

特性的用法

因為它是一個修飾器,所以呼叫方法有兩種:

1.直接使用@符號呼叫

class Dog:
 
    def __init__(self):
        self.__name = None
 
    @property
    def name(self):
        print('呼叫了get_name方法...')
        return self.__name
    @name.setter
    def name(self, val):
        print('呼叫了set_name方法...')
        self.__name = val
    @name.deleter
    def name(self):
        print('呼叫了del_name方法...')
        del self.__name
if __name__ == '__main__':
    tom = Dog()
    tom.name = 'Tom'
    print(tom.name)
    del tom.name

2.當做一個正常的函數使用

class Cat:
    def __init__(self):
        self.__name = None
    def set_name(self, name):
        print('呼叫了set_name方法...')
        self.__name = name
 
    def get_name(self):
        print('呼叫了get_name方法...')
 
        return self.__name
 
    def del_name(self):
        print('呼叫了del_name方法...')
        del self.__name
    name = property(get_name, set_name, del_name, 'this is a property name.')
if __name__ == '__main__':
    tom = Cat()
    tom.name = 'Tom'
    print(tom.name)
    del tom.name

可以看到兩種方法使用的效果都是一樣的!

特性的使用場景

重點講一下這個,正是因為有一些特定場景的存在,才會出現property這個特性的,正如之前在《Django企業開發實戰》一書中所說的那樣:若無必要,勿增實體。而python向來以簡潔優雅著稱,因為這些特定場景,所以是有必要增加property這個實體的。

主要使用場景有兩個:

1.限制一些屬性為唯讀

 在只使用@property修飾某個方法後,將其變成特定的屬性,如果不新增setter和deleter方法,那麼它就變成了唯讀屬性。比如:使用者名稱設定後就不允許修改:

class User:

    def __init__(self, username, password):
        self.__username = username
        self.password = password

    # 將username屬性限定為唯讀
    @property
    def username(self):
        return self.__username
if __name__ == '__main__':
    tom=User('tom','1234')
    print(tom.username)
    tom.username='Tom'

可以看到在唯讀狀態下,修改和刪除屬性都會失敗!

2.限定屬性的操作範圍

生活中,你可能會遇到這樣的情況,有一天你告訴你媽你想吃魚,並且是紅燒魚而不是清蒸魚,然後你媽命令你爸去買魚,同時你媽囑咐你爸說:買新鮮一點的啊,價格貴一點沒關係!然後你爸心想:這個月工資還沒發呢,能省省就省省吧,於是你爸在心中就大概確定了買一條魚的開銷區間(比如30到100)。可以看到在一條魚上餐桌之前,你和你爸媽是對它進行了精挑細選,才得以上到你的餐桌,其實就是對魚這個屬性增加了一些條件限定:型別的限定:要紅燒的而不是清蒸的;取值範圍的限定:不能太貴也不能太便宜,30到100之前剛剛好。

接下來用程式碼實現一下這個栗子:

class Fish:
 
    def __init__(self):
        self.__price = None
        self.__cook = None
    @property
    def price(self):
        return self.__price
    @price.setter
    def price(self, pri):
 
        if not isinstance(pri, int):
            raise ValueError('價格有誤!')
        if pri < 30:
            raise ValueError('太便宜了,不要!')
        elif pri > 100:
            raise ValueError('太貴了,不要!')
        else:
            self.__price = pri
            print('價格剛剛好!')
    @property
    def cook(self):
        return self.__price
 
    @cook.setter
    def cook(self,method):
 
        if method!='紅燒':
            raise ValueError('我只要紅燒魚!')
        else:
            print('耶,我最愛的紅燒魚!')
if __name__ == '__main__':
    luck_fish=Fish()
    luck_fish.price=60
    # luck_fish.cook='清蒸'
    luck_fish.cook='紅燒'

可以看到,只有當屬性的取值返回和型別是規定的範圍之內的時候,程式才會正常執行,而生活中類似這樣的栗子很多,所以這也是特性存在的意義。 

屬性和特性之間的差別和聯絡

直觀的看,特性的目的好像是把方法“屬性化”,但這樣做一點意義也沒有,如果我可以定義一個屬性,何必再額外定義一個方法,然後將其轉化成屬性呢?所以,更重要的目的就是應對一些特定場景。

從特性表現出來的性質和行為來看,它其實就是一種特定的“屬性”。只不過特性的權利提升了一點點,就好像你可以去修改這個屬性,但是能不能修改成功,就看你的上一級允不允許你修改(有沒有給你這個屬性新增限定條件),而特性的權利就擴充套件到了這個“上一級”。

屬性和特性之間的優先權

在寫程式的時候,也不乏會出現這樣的例子:屬性和特性之間重名了!那麼這個時候在使用這個重名的屬性(特性)時,程式是會報錯?還是會使用屬性的值?還是會使用特性的值?也許根據它們的名稱就可以猜出來,特性嘛,肯定就特別一些嘛,所以會優先使用特性的值!下面這個栗子完美說明:

# 關係:在同名的情況下,範例屬性會覆蓋類屬性,特性會覆蓋範例屬性
 
class Cat:
    @property
    def wow(self):
        return 'property---喵~'
if __name__ == '__main__':
    # 範例屬性會覆蓋類屬性
    jerry = Cat()
 
    # 特性會覆蓋範例屬性
    print(jerry.__dict__)  # 檢視物件的屬性字典
    jerry.__dict__['wow'] = 'normal---喵~'
    print(jerry.__dict__)
    print(jerry.wow)  # 雖然修改了jerry的wow的值,但是依舊返回的是其特性的值
    print(jerry.__dict__['wow'])
    del Cat.wow  # 刪除類特性後,返回的就是正常的屬性值
    # del Cat.wow  # 刪除特性
    print(jerry.wow)
    Cat.wow = property(lambda self: 'add a property---喵~')  # 為類新增和屬性同名的特性
    print(jerry.wow)  # 之後還是優先存取特性的值

到此這篇關於python中的property及屬性與特性之間的優先權的文章就介紹到這了,更多相關python property 內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com