__getattribute__ 官方文檔中描述如下: 該方法可以攔截對對象屬性的所有訪問企圖,當屬性被訪問時,自動調用該方法(只適用於新式類)。因此常用於實現一些訪問某屬性時執行一段代碼的特性。 需要註意的是,正式由於它攔截對所有屬性的訪問(包括對__dict__的訪問),在使用中要十分小心地避開 ...
__getattribute__
官方文檔中描述如下:
該方法可以攔截對對象屬性的所有訪問企圖,當屬性被訪問時,自動調用該方法(只適用於新式類)。因此常用於實現一些訪問某屬性時執行一段代碼的特性。
需要註意的是,正式由於它攔截對所有屬性的訪問(包括對__dict__的訪問),在使用中要十分小心地避開無限迴圈的陷阱。在__getattribute__方法中訪問當前實例的屬性時,唯一安全的方式是使用基類(超類) 的方法__getattribute__(使用super)。例如:
通過上圖中的代碼示例可以看出,一旦實現了__getattribute__方法,所有通過對象訪問的屬性(包括類屬性)都會被攔截,而直接通過類訪問類屬性則不會。
註意:當訪問的屬性不存在並重載(覆蓋基類對某方法的預設實現)了__getattribute__方法時,該方法不會主動拋出AttributeError異常。上圖中捕獲的AttributeError異常,是由基類__getattribute__方法實現並拋出。
常見的錯誤用法示例:
在實現__getattribute__方法時訪問對象自身的屬性,程式陷入無限迴圈直到崩潰。
__getattr__
官方文檔描述如下:
__getattr__方法的自動執行,需要滿足兩個條件:一是訪問對象屬性;二是觸發AttributeError異常。代碼示例如下:
上圖中,調用不存在的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__就會調用一次__setattribute__。
註意賦值語句與屬性調用的區別:self.__dict__ = {}是賦值語句,不會觸發__getattribute__方法,但觸發__setattr__方法;self.__dict__[name] = value語句,先調用self.__dict__屬性,得到dict對象後再修改其成員。
以上。