我正在尝试构建一个系统,其中基类用于所有其他对象。每个基础对象都有一个 _fields
内部字典,基类的实现可以在其中存储其信息。
基类实现非常简单:
class A(object):
def __init__(self, fields=dict()):
self._fields = fields
该类的实现可以设置 __init__
中的字段调用其super()
.
我想补充的是,这些字段可以作为属性访问,而无需添加 @property
装饰器到一大堆功能。我已经覆盖了 __getattr__
为此,在基类中如下所示:
class A(object):
def __init__(self, fields=dict()):
self._fields = fields
def __getattr__(self, name):
if hasattr(self, name):
return object.__getattribute__(self, name)
elif name in self._fields:
return self._fields.get(name)
else:
raise AttributeError
现在这个类的实现可以像这样工作:
class A_impl(A):
def __init__(self):
super(A_impl, self).__init__(
fields=dict(
val_1="foo",
val_2="",
val_3="bar",
)
)
通过创建此类的实现,您可以选择执行以下操作:
test = A_imp()
print test.val_1
print test.val_2
print test.val_3
哪个返回
foo
bar
我什至可以通过 @property
覆盖它装饰器,像这样改变类:
class A_impl(A):
def __init__(self):
super(A_impl, self).__init__(
fields=dict(
val_1="foo",
val_2="",
val_3="bar",
)
)
@property
def val_1(self):
return self._fields.get('val_1') + "_getter"
这允许我操纵数据以返回。唯一的问题是,如果我希望能够设置这些字段变量之一,我必须实现描述符 setter 函数,这还需要我创建属性描述符,这会产生大量重复工作(即我必须为所有字段定义描述符,这是我想在这里避免的。
我实现了__setattr__
基类的函数来解决以下问题:如果我手动实现描述符 setter 函数,则应选择该函数而不是默认值 self._field[name] = value
。基类现在看起来像这样(类似于 __getattr__
):
class A(object):
def __init__(self, fields=dict()):
self._fields = fields
def __getattr__(self, name):
if hasattr(self, name):
return object.__getattribute__(self, name)
elif name in self._fields:
return self._fields.get(name)
else:
raise AttributeError
def __setattr__(self, name, value):
if hasattr(self, name):
object.__setattr__(self, name, value)
elif name in self._fields:
self._fields[name] = value
else:
raise AttributeError
现在,如果我再次运行相同的代码测试:
test = A_imp()
print test.val_1
print test.val_2
print test.val_3
它立即陷入无限循环,从 __setattr__
开始但跳到__getattr__
紧随其后并继续循环。
为此,我在 stackoverflow 上阅读了很多问题,但无法弄清楚,这就是为什么我构建这个测试用例来清楚地弄清楚它。希望有人能够为我澄清这一点并帮助我解决问题。
最佳答案
除了实际尝试检索对象之外,无法检查对象是否具有属性。因此,
hasattr(self, 'val_1')
实际上尝试访问self.val_1
。如果self.val_1
无法通过正常方式找到,它依赖 __getattr__
,调用 hasattr
再次无限递归。
hasattr
实际上捕获了任何异常,包括 RuntimeError: maximum recursion depth exceeded
,并返回 False
,因此其具体表现方式取决于有多少 __getattr__
调用是嵌套的。一些__getattr__
来电打object.__getattribute__
案例并提出 AttributeError
;有些点击了elif name in self._fields: return self._fields.get(name)
案件。如果self._fields
存在,这会返回一个值,但与您的 __setattr__
,有时self._fields
不存在!
当您的 __setattr__
尝试处理 self._fields
分配于__init__
,它调用 hasattr(self, '_fields')
,调用 __getattr__
。现在一些__getattr__
调用进行两次递归 __getattr__
来电,一入hasattr
和 elif name in self._fields
中的一个。自 hasattr
正在捕获异常,这会导致递归调用在递归深度上呈指数级增长,而不是看起来很快就能工作或引发异常。
不要使用hasattr
在__getattr__
或__getattribute__
。一般来说,在处理属性访问的方法中访问属性的所有尝试都要非常小心。
关于python - 组合 __setattr__ 和 __getattr__ 会导致无限循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45965216/