python __getattribute__、__getattr__、__setattr__

__getattribute__

官方文檔中描述如下:

該方法可以攔截對對象屬性的所有訪問企圖,當屬性被訪問時,自動調用該方法(只適用於新式類)。因此常用於實現一些訪問某屬性時執行一段代碼的特性。

需要注意的是,正式由於它攔截對所有屬性的訪問(包括對__dict__的訪問),在使用中要十分小心地避開無限循環的陷阱。在__getattribute__方法中訪問當前實例的屬性時,唯一安全的方式是使用基類(超類) 的方法__getattribute__(使用super)。例如:

通過上圖中的代碼示例可以看出,一旦實現了__getattribute__方法,所有通過對象訪問的屬性(包括類屬性)都會被攔截,而直接通過類訪問類屬性則不會。

注意:當訪問的屬性不存在並重載(覆蓋基類對某方法的默認實現)了__getattribute__方法時,該方法不會主動拋出AttributeError異常。上圖中捕獲的AttributeError異常,是由基類__getattribute__方法實現並拋出。

常見的錯誤用法示例:

在實現__getattribute__方法時訪問對象自身的屬性,程序陷入無限循環直到崩潰。

__getattr__

官方文檔描述如下:

__getattr__方法的自動執行,需要滿足兩個條件:一是訪問對象屬性;二是觸發AttributeError異常。代碼示例如下:

class Man(object):
	def __init__(self,name,age):
		self.name = name
		self.age = age

	def __getattribute__(self, item):
		print("攔截__getattribute__",item)
		if item != "name":
			raise AttributeError()
		return super().__getattribute__("name")

	def __getattr__(self, item):
		print("執行__getattr__")
		return self.name

man = Man("天浩",1)
print(man.age)

攔截__getattribute__ age
執行__getattr__
攔截__getattribute__ name
天浩

__getattribute__的坑

形成死循環 是因爲在__getarrtibute__中調用self.a

self.a 實際上就是調用 obj.__getarrtibute__("a") 形成死循環

obj.c 實際上就是調用 obj.__getarrtibute__("c")

 

 

 

上圖中,調用不存在的job屬性首先調用__getattribute__方法(如果該方法未定義,會調用基類的__getattribute__方法),觸發AttributeError異常並自動捕獲,然後才調用__getattr__方法。

錯誤用法示例如下:

重載了__getattribute__方法,卻沒有主動拋出AttributeError異常的機制,或者拋出一個其它類型的異常,__getattr__方法都不會執行。

__setattr__

試圖給屬性賦值時自動調用該方法,例如:

之所以會執行三次print函數,是因爲在__init__方法中,對象A初始化時給屬性name和age賦值時,觸發了__setattr__方法。使用該方法是同樣需要十分小心避免無限循環陷阱。

錯誤用法示例如下:

可以看出,在__setattr__方法中,不能直接給屬性賦值,而通常的做法是使用__dict__魔法屬性。__dict__屬性是一個字典,所有的實例屬性都存儲在這個字典中,而修改__dict__字典中的鍵值對成員不會觸發__setattr__方法,這裏應注意與直接修改__dict__的值的區別。

注意:如果定義__setattr__方法的同時定義了__getattribute__方法,那麼在修改__dict__字典中的鍵值對時,由於調用了self.__dict__屬性,同樣會觸發__getattribute__方法,使用時應格外小心。代碼示例如下:

上圖示例代碼中,每調用一次__setattr__就會調用一次__getattribute__。

注意賦值語句與屬性調用的區別:self.__dict__ = {}是賦值語句,不會觸發__getattribute__方法,但觸發__setattr__方法;self.__dict__[name] = value語句,先調用self.__dict__屬性,得到dict對象後再修改其成員,因此會觸發__getattribute__方法。 

以上。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章