python - python copy.deepcopy时的递归错误

标签 python getattr

我在 python 中遇到了问题。

我有一个自定义类 __getattr__

class ChoiceNumToName(object):
    def __init__(self, django_choice_tuple):
        self.ods_choice_tuple = django_choice_tuple
        self.choice_data = {}
        self.choice_point = -1
        for choice_value, choice_name in django_choice_tuple:
            self.choice_data.setdefault(choice_name, choice_value)

    def __getattr__(self, item):
        if item in self.choice_data:
            return self.choice_data[item]
        else:
            raise AttributeError("no attribute %s" % item)

    def __str__(self):
        return str(self.ods_choice_tuple)

    def __iter__(self):
        self.choice_point = -1
        return self

    def __next__(self):
        self.choice_point += 1
        try:
            return self.ods_choice_tuple[self.choice_point]
        except IndexError:
            raise StopIteration()

当我执行此操作时

a = ChoiceNumToName((
    (1, "running"),
    (2, "stopped"),
))
b = copy.deepcopy(a)

它引发 RecursionError: maximum recursion depth exceeded while calling a Python object

解决这个问题的例子,将__getattr__函数改成这个

def __getattr__(self, item):
    if item == "__setstate__":
        raise AttributeError(item)
    if item in self.choice_data:
        return self.choice_data[item]
    else:
        raise AttributeError("no attribute %s" % item)

效果不错。

我从这里知道这个解决方案 https://github.com/python-babel/flask-babel/commit/8319a7f44f4a0b97298d20ad702f7618e6bdab6a

但谁能告诉我为什么?

最佳答案

TLDR:你的 __getattr__choice_data 之前调用已被添加到实例字典中,这导致它无休止地递归。解决该问题的更好方法是立即为以 __ 开头的任何属性引发 AttributeError捕捉任何其他特殊或内部属性。

发生这种情况是因为复制对象时 __init__方法未被调用。相反,创建了一个新的空对象。这个新对象有一个空的 __dict__ . Python 的 pickle 协议(protocol)(也用于复制模块)有一个钩子(Hook) __setstate__允许自定义应用状态(通常只是 __dict__ 的内容,但是,例如,如果提供 __getstate__,它可以是任何对象)。查看该 Hook 是否存在 hasattr(newobj, '__setstate__')被称为 which,因为没有任何 __setstate__在 MRO 或 __dict__ 中导致你的__getattr__被称为。你的__getattr__然后尝试访问 self.choice_data但是,正如我们之前提到的 __dict__目前是空的。这会导致 __getattr__再次调用方法以获取 choice_data启动无限递归的属性。

特殊套管__setstate__通过提早退出查找 __setstate__ 来阻止递归被触发.当失败时,默认的复制机制生效,初始化新对象的 __dict__。从国家。我心目中只有特殊外壳__setstate__不是最好的解决方案。我认为最好立即为任何特殊或内部属性引发 AttributeError,即以 __ 开头的那些,因为这可以防止其他奇怪情况的发生。另一种可能性是避免在 __getattr__ 中使用属性查找。通过写作 self.__dict__['choice_data']object.__getattribute__(self, 'choice_data') .您还可以确保 choice_data将通过实现 __new__ 出现并将其分配给那里的对象。

关于python - python copy.deepcopy时的递归错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47299243/

相关文章:

python - 在循环中使用字母表作为计数器

python - 在 Python 中使用 getattr/setattr 访问列表项

python - 从字符串中获取类对象

python getattr 内置方法执行默认参数

python - 为什么在 numpy.arange() 上使用 numpy.array()?不是多余的吗?

python - Sklearn __init__.py 错误

python - 如何存储相同的参数以供多个函数使用

python - 将 pandas 字符串列与缺失值组合

python - 使用 getattr 获取方法的对象引用

Python - 如何从给定的字符串调用实例?