python时出现递归错误复制.deepcopy

2024-05-29 05:54:37 发布

您现在位置:Python中文网/ 问答频道 /正文

我在python中遇到了一个问题。在

我有一个类whis custom__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()

当我执行此操作时

^{pr2}$

它引起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

但谁能告诉我为什么吗?在


Tags: djangoinselfdatareturnifdefitem
2条回答

方法__getstate__和{}用于酸洗操作。为什么这很重要?从Python docs on copying

Classes can use the same interfaces to control copying that they use to control pickling.

通过定义一个引用自身的__setstate__,您已经创建了一个递归对象,因此产生了RecursionError。在

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

发生这种情况是因为复制对象时,__init__方法没有被调用。而是创建一个新的空对象。这个新对象有一个空的__dict__。Python的pickle协议(也用于copy模块)有一个钩子__setstate__,它允许定制应用状态(通常只是__dict__的内容,但是,如果提供了__getstate__,它可以是任何对象)。为了查看这个钩子是否存在,hasattr(newobj, '__setstate__')被调用,因为MRO和__dict__中都没有__setstate__,所以会调用你的{}。然后您的__getattr__试图访问self.choice_data,但是,正如我们前面提到的,__dict__当前是空的。这将导致再次调用__getattr__方法以获得启动无限递归的choice_data属性。在

特殊的大小写__setstate__阻止了由于查找__setstate__而提前释放而触发的递归。如果失败,复制的默认机制将生效,该机制从状态初始化新对象的__dict__。在我看来,只有特殊的外壳并不是最好的解决方案。我认为对于任何特殊的或内部的属性,即以__开头的属性,最好立即引发AttributeError,因为这样可以防止其他奇怪的情况发生。另一种可能是通过编写self.__dict__['choice_data']或{}来避免在__getattr__中使用属性查找。您还可以通过实现__new__并将其分配给那里的对象来确保choice_data将出现。在

相关问题 更多 >

    热门问题