__setattr__,__delattr__,__getattr__ 到底幹了什麼?
公眾號:pythonislover
Python大數據與SQL優化筆記
__setattr__,__delattr__,__getattr__是python中自帶的三個函數,可能有人對這幾個函數還不太熟悉,今天我們說說他們是幹什麼用的。
我們先看一段代碼
class Cat:
class_level = 貴族
def __init__(self,name,type,speed,age):
self.name = name
self.type= type
self.speed = speed
self.age = age
def run(self):
print(%s歲的%s%s正在以%s的速度奔跑 % (self.age,self.type,self.name,self.speed))
def __getattr__(self, item):
print(你找的屬性不存在)
def __setattr__(self, key, value):
print(你在設置屬性)
def __delattr__(self, item):
print(你在刪除屬性)
xiaohua = Cat(小花,波斯貓,10m/s,10)
xiaohua.run() #10歲的波斯貓小花正在以10m/s的速度奔跑
結果:
你在設置屬性
你在設置屬性你在設置屬性你在設置屬性你找的屬性不存在你找的屬性不存在你找的屬性不存在你找的屬性不存在
None歲的NoneNone正在以None的速度奔跑可以看到__setattr__和__getattr__ 被觸發了,為什麼呢? 我們上面的代碼只是做了一個實例化,還記得實例化的時候要做什麼嗎,就是找到__init__函數設置實例的數據屬性,所以這裡__setattr__被觸發了,但是__getattr__ 為什麼被觸發了呢? 因為我們的__setattr__函數下面只是列印了一行,並沒有任何的其他賦值操作,所以__init__裡面的數據屬性沒有被賦值,在對象調用run方法的時候就找不到self.age,self.type,self.name,self.speed這些屬性,所以就會觸發_getattr__方法。
下面總結下這3個方法的使用場景:
#__setattr__添加/修改屬性會觸發它的執行
#__getattr__只有在使用點調用屬性且屬性不存在的時候才會觸發
#__delattr__刪除屬性的時候會觸發
如果我們在Class沒有定義這3個方法,那麼系統會用Python自帶的內部函數,如果我們在類裡面自定義了這3個函數,那麼python會先調用我們自己定義的這3個函數
上面的__getattr__之所以被觸發就是因為__setattr__函數下面只是列印了一行,並沒有任何的其他賦值操作。
下面我看再看看, 我們怎麼完善上面那部分代碼,讓__setattr__成功賦值,讓__getattr__不被觸發,還是__delattr__怎麼觸發。
class Cat:
class_level = 貴族
def __init__(self,name,type,speed,age):
self.name = name
self.type= type
self.speed = speed
self.age = age
def run(self):
print(%s歲的%s%s正在以%s的速度奔跑 % (self.age,self.type,self.name,self.speed))
def __getattr__(self, item):
print(你找的屬性不存在)
def __setattr__(self, key, value):
print(你在設置屬性)
# self.key=value #這種方法不行,會產生無限遞歸了,因為他本身self.key=value也會觸發__setattr__
self.__dict__[key] = value #我們在給對象屬性賦值的時候,內部原理就是操作對象的__dict__字典,所以我們可以直接操作對象的字典實現屬性賦值
#刪除屬性的時候會觸發
def __delattr__(self, item):
print(你在刪除屬性)
# del self.item #無限遞歸了,和上面的__setattr__原理一樣
self.__dict__.pop(item)
#實例化,會到__init__函數里去給實例數據屬性賦值
xiaohua = Cat(小花,波斯貓,10m/s,10)
#結果:
# 你在設置屬性
# 你在設置屬性
# 你在設置屬性
# 你在設置屬性
xiaohua.run() #10歲的波斯貓小花正在以10m/s的速度奔跑
# 結果:
# 10歲的波斯貓小花正在以10m/s的速度奔跑
#我們看到__setattr__正常賦值了
#實例數據屬性賦值操作,觸發__setattr__方法
xiaohua.weight = 5kg
print(xiaohua.__dict__)
# 結果:
# 你在設置屬性
# {name: 小花, type: 波斯貓, speed: 10m/s, age: 10, weight: 5kg}
#刪除對象的數據屬性操作,觸發__delattr__方法
del xiaohua.weight
print(xiaohua.__dict__)
# 結果:
# 你在刪除屬性
# {name: 小花, type: 波斯貓, speed: 10m/s, age: 10}
#調用對象的不存在的數據屬性,會觸發__getattr__
xiaohua.size
# 結果:
# 你找的屬性不存在
上面我們就理清楚了這三個內置函數的作用和什麼時候會被調用。
下面我們說一個__getattr__實現的一個授權案例:
授權:授權是包裝的一個特性, 包裝一個類型通常是對已存在的類型的一些定製,這種做法可以新建,修改或刪除原有產品的功能。其它的則保持原樣。授權的過程,即是所有更新的功能都是由新類的某部分來處理,但已存在的功能就授權給對象的默認屬性。(解釋來自於網路)
實現授權的關鍵點就是覆蓋__getattr__方法
授權的大概作用就是對一些系統級的方式,實現自定義的一些改變,比如下面的這個案例
下面的案例實現的功能:
- 在原生List類的基礎下,實現append方法只能插入int類型數據
- 執行新功能獲取列表的中間index值
class List:
def __init__(self,onelist):
self.onelist=onelist #
#基於原生的List類,修改原生的append方法,只能插入int類型的數據
def append(self, p_object):
if not isinstance(p_object,int):
raise TypeError(must be int)
self.onelist.append(p_object)
@property
def getmid(self):
新增自己的方法
index = len(self.onelist) // 2
return self.onelist[index]
def __getattr__(self, item):
return getattr(self.onelist,item) #這是關鍵點,如果item在本類中不存在,會去原生的List類裡面取尋找item方法
l=List([1,2,3]) #等價於l=[1,2,3]
l.append(4)
print(l)
#l.append(abc) #報錯,必須為int類型
#新增自己的方法,獲取列表中間的index
print(l.getmid)
#基於授權,獲得insert方法,insert方法在本類中沒有實現,所以回去原生的List中尋找insert方法
#list.insert(index, obj)
l.insert(0,9)
print(l)
推薦閱讀: