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

标签 python class infinite-loop getattr setattr

我正在尝试构建一个系统,其中基类用于所有其他对象。每个基础对象都有一个 _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__来电,一入hasattrelif name in self._fields 中的一个。自 hasattr正在捕获异常,这会导致递归调用在递归深度上呈指数级增长,而不是看起来很快就能工作或引发异常。


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

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

相关文章:

java - 如何使用 Visual Studio Code 在 java 中导入内容

PHP connection_aborted() 无法正常工作

python - 如何将实例对象保存到硬盘

python - pytorch 是否对其计算图进行急切修剪?

python - 让 lambda 在循环中生成唯一数据

python - tensorflow 中的参数化混合密度网络协方差

java - 在 IntelliJ 中,查看类/接口(interface)的所有后代的快捷方式?

.htaccess - 如何使用 .htaccess 将所有流量 302 重定向到主页?避免无限重定向循环?

java - 为什么我会收到输入无效无限循环?

python - Slack API - 用于检索斜杠命令的队列