1. 程式人生 > >17、類的自定義屬性訪問及動態屬性設定

17、類的自定義屬性訪問及動態屬性設定

前言:本文主要介紹python類的一些自定義屬性訪問的方法,以及類的動態屬性設定即python的內建函式setattr()

自定義屬性訪問

什麼是屬性?下面的例子a和b是屬性嗎?不是,他們是全域性變數,屬性(attribute)是類中的成員變數,也可以理解為屬性就是類變數。

a = 11234
b = 'python'

類中的變數是靜態變數,類可以直接訪問,python是一門動態語言,任何例項物件都可以動態地新增或刪除屬性,一個類定義了一個作用域,類例項物件也引入了一個作用域,這與類定義的作用域是不同的。在類例項物件中查詢屬性的時候,首先在例項自己的作用域中查詢,如果沒有找到,則再去類定義的作用域中查詢。在對類例項屬性進行賦值的時候,實際上會在類例項定義的作用域中新增一個屬性或修改一個屬性,但並不會影響到對應類中定義的同名屬性。那麼從訪問屬性到返回結果的過程是怎麼運作的呢?是通過下面幾個魔術方法來實現的。

相關方法的使用

  • __getattribute__:查詢屬性時會先觸發該方法進行屬性查詢
  • __getattr__:查詢屬性沒找到的時候觸發
  • __setattr__:設定屬性的時候觸發
  • __delattr__:刪除屬性的時候觸發

為了直觀地感受例項訪問屬性時都做了什麼,我們看一下下面的例子:

class TestCase:
    att_1 = 'hello'  # 定義類屬性
    att_2 = 'python'

    def test_func(self):  # 定義方法
        print("這是一個方法")

    def __getattribute__(self, item):
        # 屬性訪問攔截器:當物件訪問屬性時,會自動觸發這個方法,由這個方法來決定返回的屬性值
        # 應用場景:訪問不存在的屬性時,不希望它報錯,可以用try--except來捕獲異常返回一個值或提示資訊
        # try:
        #     return super().__getattribute__(item)  # 呼叫父類真正的__getattribute__方法返回正確的屬性值
        # except AttributeError:
        #     print(item,':該屬性不存在!')
        # 如果使用了try方法就不會觸發__getattr__方法了,因為找不到屬性時的異常已經在這裡被捕獲了
        print("我是__getattribute__,我正在工作")
        return super().__getattribute__(item)    # 呼叫父類的方法,返回找到的結果,不呼叫就不會返回

    def __getattr__(self, att_name):
        # 當物件訪問屬性時,屬性不存在,引發異常,會被__getattr__方法捕獲
        # 然後執行該方法的程式碼,相當於自帶捕獲異常
        print("我是__getattr__,我正在工作")
        return att_name + "這是我要找的東西,但是我找不到"

    def __setattr__(self, att_name, value):
        # 設定屬性的時候就會觸發該方法
        print("我是__setattr__,我正在工作")
        super().__setattr__(att_name, value)    # 呼叫父類的方法,設定屬性,不呼叫就不會真的設定屬性

    def __delattr__(self, att_name):
        print("我是__delattr__,我正在工作")
        print("這是我即將刪除的東西{}".format(att_name))
        super().__delattr__(att_name)   # 呼叫父類的方法,刪除屬性,不呼叫就刪除不了


res = TestCase()  # 例項化物件
print('-----------------訪問屬性----------------')
print(res.att_1)    # 訪問類屬性,通過執行結果看res例項物件訪問屬性時通過了方法__getattribute__

print('\n-----------------訪問不存在的屬性----------------')
print(res.att_3)

print('\n-----------------設定屬性----------------')
res.att_3 = 'new_attr'

print('\n-----------------刪除屬性----------------')
del res.att_3

執行結果:

C:\software\python\python.exe D:/learn/python/test.py
-----------------訪問屬性----------------
我是__getattribute__,我正在工作
hello

-----------------訪問不存在的屬性----------------
我是__getattribute__,我正在工作
我是__getattr__,我正在工作
att_3這是我要找的東西,但是我找不到

-----------------設定屬性----------------
我是__setattr__,我正在工作

-----------------刪除屬性----------------
我是__delattr__,我正在工作
這是我即將刪除的東西att_3

Process finished with exit code 0

從結果中,我們可以看到當例項訪問屬性時,就會自動觸發__getattribute__()方法,然後由這個方法來決定返回的屬性值;當例項訪問不存在的屬性時,會引發異常,而這個異常會被__getattr__()方法捕獲,然後執行該方法的程式碼,相當於自帶捕獲異常;同理,在設定屬性或刪除屬性時,會自動觸發對應的__setattr__()方法或__delattr__()方法。

但需要注意的是,在自定義這些方法的時候,一定要記得呼叫對應的父類方法,將結果返回,否則只是執行了你自定義的東西並沒有真正去做它本身要去做的事情。

動態屬性設定

setattr()是python的一個內建函式,用於動態設定例項屬性,語法:setattr(object, name, value)