gpt4 book ai didi

python - 组合 __setattr__ 和 __getattr__ 导致无限循环

转载 作者:太空宇宙 更新时间:2023-11-04 00:30:35 25 4
gpt4 key购买 nike

我正在尝试构建一个系统,其中一个基类用于所有其他对象。每个基对象在内部都有一个 _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"

这让我可以操纵返回的数据。唯一的问题是,如果我想能够设置这些字段变量之一,我必须实现描述符设置函数,这还需要我制作属性描述符,这会产生大量重复工作(即我必须为我的所有字段定义描述符,这是我想在这里避免的。

我为基类实现了 __setattr__ 函数来解决以下问题:如果我手动实现描述符 setter 函数,则应该选择默认值 self._field[name] = 值。基类现在看起来像这样(类似于 __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;一些在 self._fields: return self._fields.get(name) 案例中命中了 elif name。如果 self._fields 存在,这将返回一个值,但是对于您的 __setattr__,有时 self._fields 不存在!


当您的 __setattr__ 尝试处理 __init__ 中的 self._fields 赋值时,它会调用 hasattr(self, '_fields' ),它调用 __getattr__。现在一些 __getattr__ 调用进行两次递归 __getattr__ 调用,一次在 hasattr 中,一次在 elif name in self._fields 中.由于 hasattr 正在捕获异常,这会导致递归调用在递归深度呈指数级增长,而不是快速地工作或引发异常。


不要在 __getattr____getattribute__ 中使用 hasattr。一般来说,在处理属性访问的方法中访问属性的所有尝试都要非常小心。

关于python - 组合 __setattr__ 和 __getattr__ 导致无限循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45965216/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com