我正在尝试构建一个系统,其中基类用于每个其他对象。每个基础对象都在内部有一个_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的很多问题并且无法解决这个问题,这就是为什么我构建这个测试用例来干净利落地解决它。希望有人能够为我澄清这一点并帮助我解决问题。
答案 0 :(得分:3)
无法检查对象是否具有除实际尝试检索之外的属性。因此,
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
正在捕获异常,因此这会导致递归调用在递归深度呈指数形式,而不是很快就会起作用或引发异常。
请勿在{{1}}或hasattr
中使用__getattr__
。通常,在处理属性访问的方法中访问属性的所有尝试都要非常小心。