前言:本文主要介紹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)
🍊 參數1:object-對象
🍋 參數2:name-給對象要設置的屬性名(字符串類型)
🍍 參數3:value-屬性值
class TestCase:
"""這是一個存放測試用例數據的類"""
pass
cases = [
{'case_id': 1, 'data': '123', 'actual': '不通過', 'excepted': '通過'},
{'case_id': 2, 'data': '123', 'actual': '通過', 'excepted': '通過'},
{'case_id': 3, 'data': '123', 'actual': '不通過', 'excepted': '通過'},
{'case_id': 4, 'data': '123', 'actual': '通過', 'excepted': '通過'},
]
res = [] # 新建空列表用於存放結果
for i in cases:
case = TestCase() # 創建一個實例對象
for k, v in i.items():
# 把毎條用例數據中字典的key和value設成該實例的屬性與值,比如:case_id = 1,data = 123等,把一個字典的全部鍵值對設為一個實例的屬性
setattr(case, k, v)
res.append(case) # 把創建的對象存到結果中
print(res) # 存放的是對象,共4個
print("\n按字段讀取第一條測試用例數據:")
print("case_id:", res[0].case_id) # 讀取第一個對象的case_id屬性值
print("data:", res[0].data)
print("actual:", res[0].actual)
print("excepted:", res[0].excepted)
print("\n讀取完整的第一條測試用例數據:")
print(res[0].__dict__)
運行結果:
C:\software\python\python.exe D:/learn/python/test.py
[<__main__.TestCase object at 0x0000019F15F573C8>, <__main__.TestCase object at 0x0000019F15F57C18>, <__main__.TestCase object at 0x0000019F16009C18>, <__main__.TestCase object at 0x0000019F16009C50>]
按字段讀取第一條測試用例數據:
case_id: 1
data: 123
actual: 不通過
excepted: 通過
讀取完整的第一條測試用例數據:
{'case_id': 1, 'data': '123', 'actual': '不通過', 'excepted': '通過'}
Process finished with exit code 0
上面的運行結果中,像<main.TestCase object at 0x0000019F15F573C8>這就是一個TestCase()類的實例對象,這是它的內存地址,setattr()
動態設置屬性,就相當於case.case_id=1給實例設置一個屬性,只是上面所舉例是循環地把整個列表的數據拆分成4個實例的屬性。
同樣的,python中對應還有一個getattr(object, name)
內置函數,它是用於返回一個實例的屬性,需要兩個參數,一個是對象名,一個是屬性名,返回該屬性的屬性值,這里不再舉例,自己使用一下吧!
另外,上述例子中最后還用到了一個__dict__
對象屬性,它是用於獲取實例對象的所有屬性,並以字典的形式返回。