我整天都在为此奋斗,并进行了大量的谷歌搜索。我遇到了似乎是继承问题。
我有一个名为 BaseClass 的类,它为我做了一些简单的事情,比如设置日志默认值、保存日志和管理只读属性。过去我对这门课没有任何问题,但我可以说我现在只使用 python 几个月了。此外,我怀疑有几点很重要:
- 所有过去的继承都是单一继承。换句话说,我继承了 BaseClass 但继承 BaseClass 的类不会被另一个类继承。在今天的问题中,我将 BaseClass 继承到 BaseFormData,然后继承到 July2013FormData,然后继承到 Jan2014FormData。所以显然现在有更多的层次。
- 我没有尝试覆盖
__getattr__
或__getattribute__
在过去继承 BaseClass 的任何类中。今天我是。BaseClass 有__getattribute__
自己的方法来管理只读属性的获取。BaseFormData 类有一个__getattr__
方法,因为我读过这是无需显式声明所有属性即可提供对数据的访问的正确方法。正在加载的表单数据中有十几条或更多条数据(取决于版本),因此我明确声明了一些重要或别名的属性,而其余的则由__getattr__
.
这是__getattribute__
来自 BaseClass 的:
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__
or中。__getattribute__
但是,我需要__getattribute__
在 BaseClass 中使用才能使只读功能正常工作。
而__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
简短的版本是我整天都在为此奋斗,因为在这里或那里进行调整,我得到了不同的反应。大多数时候__getattr__
BaseFormData 从未被调用过。其他时候我得到这个递归错误。其他时候我可以让它工作,但后来我添加了一些小东西,一切又坏了。
__getattribute__
关于继承以及__getattr__
调用and 的顺序,我显然缺少一些东西。我今天对代码进行了很多调整,如果没有记错的话,我不能__getattribute__
在 BaseFormData 和 BaseClass 中有一个,但我不记得我脑海中出现的错误。
我猜递归问题源于以下行__getattr__
:
tmp = super(BaseFormData, self).__getattribute__(name)
显然,我想要做的是首先查看当前类,然后转到 BaseClass__getattribute__
以检查只读属性。
任何帮助是极大的赞赏。
------------ 一些调整的结果.... ------------
所以我将 BaseFormClass 更改__getattr__
为__getattribute__
它似乎在 BaseClass 之前运行,__getattribute__
这是有道理的。
然而,它导致了无限递归,我认为这可能是由于__init__
BaseFormClass 和 BaseFormClass 的子类中某些事情发生的顺序。然后似乎是由于 __READ_ONLY 创建得太晚了,我也修复了这个问题。
最终,_raw
是在子类中而不是在 BaseClass 中,所以我删除了对_raw
BaseClass 中的任何引用_getattribute__
。我接受了 rchang 关于将 self 更改为 object 的建议,这对 BaseClass 有所帮助,__getattribute__
但似乎在 BaseFormData 中引起了问题。我已经使用以下代码从解释器中获得了“get”部分:
基类:
def __getattribute__(self, attr):
try:
# Default behaviour
return object.__getattribute__(self, attr)
except:
return self.__READ_ONLY[attr]
BaseForm类:
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 。
不过,这似乎是一个 hack。有没有标准的方法来做到这一点?