我一整天都在與這個戰鬥,並且做了大量的Google搜索。我有什麼似乎是繼承問題。Python繼承和__getattr__&__getattribute__
我有一個名爲BaseClass的類,它爲我執行一些簡單的事情,比如設置日誌記錄默認值,保存日誌和管理只讀屬性。在過去,我對這門課沒有任何問題,但我可以說我現在只用了幾個月的python。此外,還有一些我認爲很重要的注意事項:
- 所有過去的繼承都是單一的繼承。換句話說,我繼承了BaseClass,但繼承BaseClass的類不會被另一個類繼承。在今天的問題中,我將BaseClass繼承到BaseFormData中,然後將它繼承到July2013FormData,然後繼承到Jan2014FormData。所以顯然現在有更多的圖層。
- 我一直沒有試圖在過去繼承BaseClass的任何類中重寫
__getattr__
或__getattribute__
。今天我是。 BaseClass有自己的__getattribute__
方法來管理獲取只讀屬性。 BaseFormData類有一個__getattr__
方法,因爲我讀過這是提供數據訪問的正確方法,而不必顯式聲明所有屬性。被加載的表單數據中有十幾個或更多的數據(取決於版本),所以我明確聲明瞭一些重要或別名的屬性,其餘的則由__getattr__
來處理。
這裏是BaseClass的將__getattribute__
:
def __getattribute__(self, attr):
try:
# Default behaviour
return object.__getattribute__(self, attr)
except:
try:
return self.__READ_ONLY[attr]
except KeyError:
lcAttr = php_basic_str.strtolower(attr)
return self._raw[lcAttr]
使用self._raw
的線是有完全是因爲繼承BaseClass的類時常使用_raw
。 BaseClass不需要_raw
。理想情況下,參考_raw
只會出現在繼承類的__getattr__
或__getattribute__
中。但是,我需要在BaseClass中使用__getattribute__
才能使「只讀」功能正常工作。
而且__getattr__
從BaseFormData:
def __getattr__(self, name):
"""
Return a particular piece of data from the _raw dict
"""
# All the columns are saved in lowercase
lcName = s.strtolower(name)
try:
tmp = self._raw[lcName]
except KeyError:
try:
tmp = super(BaseFormData, self).__getattribute__(name)
except KeyError:
msg = "'{0}' object has no attribute '{1}'. Note that attribute search is case insensitive."
raise AttributeError(msg.format(type(self).__name__, name))
if tmp == 'true':
return True
elif tmp == 'false':
return False
else:
return tmp
這是我收到的錯誤:
File "D:\python\lib\mybasics\mybasics\cBaseClass.py", line 195, in __getattribute__
return self.__READ_ONLY[attr]
RuntimeError: maximum recursion depth exceeded while calling a Python object
簡短的版本是,我已經打了一整天這一點,因爲這裏有一個調整或者我得到不同的迴應。從BaseFormData大多數時間__getattr__
永遠不會被調用。其他時候我得到這個遞歸錯誤。其他時間我可以開始工作,但我添加了一些小東西,一切都再次打破。
有些東西我明顯遺漏了繼承問題以及調用__getattribute__
和__getattr__
的順序。我今天做了很多代碼調整,如果內存服務,我不能在BaseFormData和BaseClass中使用__getattribute__
,但我不記得我的頭頂上的錯誤。
我猜遞歸問題源於該行__getattr__
:
tmp = super(BaseFormData, self).__getattribute__(name)
顯然,正是我想要做的就是看在當前類第一,然後去BaseClass的__getattribute__
爲了檢查只讀屬性。
任何幫助,非常感謝。
了一些調整.... -----------
的-----------結果讓我改變BaseFormClass __getattr__
到__getattribute__
,它似乎在BaseClass的__getattribute__
之前運行,這是有道理的。
但是它導致了無限遞歸,我認爲這可能是由於BaseFormClass的__init__
和BaseFormClass的子類中的某些事情發生的順序。然後,這似乎是由於__READ_ONLY被創建得太晚,我也修復了這個問題。
最終,_raw
在不在BaseClass中的子類中,所以我刪除了BaseClass的_getattribute__
中對_raw
的任何引用。我採取了rchang的建議,將自己改爲對象,這有助於BaseClass的__getattribute__
,但似乎會導致BaseFormData出現問題。我已經得到了 「得到」 部分從解釋與下面的代碼工作:
的BaseClass:
def __getattribute__(self, attr):
try:
# Default behaviour
return object.__getattribute__(self, attr)
except:
return self.__READ_ONLY[attr]
BaseFormClass:
def __getattribute__(self, name):
# All the columns are saved in lowercase
lcName = s.strtolower(name)
try:
# Default behaviour
return object.__getattribute__(self, name)
except:
try:
tmp = object.__getattribute__(self, '_raw')[lcName]
except KeyError:
try:
tmp = BaseClass.__getattribute__(self, name)
except KeyError:
msg = "'{0}' object has no attribute '{1}'. Note that attribute search is case insensitive."
raise AttributeError(msg.format(type(self).__name__, name))
if tmp == 'true':
return True
elif tmp == 'false':
return False
else:
return tmp
諷刺的是這已經創造了另一個__getattribute__
問題雖然。當我嘗試覆蓋只讀屬性時,日誌警告觸發,但在撥打self.__instanceId
時失敗。這個日誌警告昨天工作正常(當我可以讓類實例化沒有錯誤)。我可以實例化類是這樣的:
a = Jan2014Lead(leadFile)
,並獲得實例ID是這樣的:
a.__instanceId
Out[7]: '8c08dee80ef56b1234fc4822627febfc'
下面是實際的錯誤:
File "D:\python\lib\mybasics\mybasics\cBaseClass.py", line 255, in write2log
logStr = "[" + self.__instanceId + "]::[" + str(msgCode) + "]::" + msgMsg
File "cBaseFormData.py", line 226, in __getattribute__
raise AttributeError(msg.format(type(self).__name__, name))
AttributeError: 'Jan2014Lead' object has no attribute '_BaseClass__instanceId'. Note that attribute search is case insensitive.
------- ----得到它的工作,但似乎hacky .... -----------
所以上面的錯誤是從_READ_ONLY中尋找_BaseClass__instanceId但__instanceId在_READ_ONLY中。所以我簡單地修剪了傳遞的attr字符串,從頭開始刪除_BaseClass。
雖然這看起來像一個黑客。有沒有一個標準的方法來做到這一點?
我不清楚你的'__READ_ONLY'字典是用於什麼。正如你現在實現'__getattribute__',只有在普通的查找失敗後纔會被檢查,這意味着(除非你有一個'__setattr__'方法你沒有提到),常規的實例變量可以分配給它們,將優先於字典的值。也許你應該單獨離開'__getattribute__'並且編寫一個'__setattr__'方法來替代某些名字。 – Blckknght 2014-12-03 06:48:16
@Blckknght我已經有一個'__setattr__',當試圖覆蓋只讀attr時引發異常。我只是不認爲這與這個問題有關。我在第一週寫了BaseClass,所以我正在學習Python,'__getattribute__'和'__setattr__'的組合是我能夠按照我的預期工作的唯一方法。我可能需要重新訪問它,但現在'__setattr__'不是問題。 – 2014-12-03 17:35:50