__getattr __()的无限递归 - 但为什么它甚至被调用一次?

时间:2016-01-22 15:12:51

标签: python recursion private-members getattr

我有一个由(相关部分)组成的课程:

class satellite(object):
    def __init__(self, orbit, payload=None, structural_mass=0, structural_price=0):
        self.__all_parts = []
        self.orbit = orbit
        self.payload = payload
        self.structural_mass = structural_mass
        self.structural_price = structural_price

    def __getattr__(self, item):
        found = False
        v = 0
        for i in self.__all_parts:
            t = getattr(i, item, None)
            if t is not None:
                v += t
                found = True
        if found:
            return v
        else:
            raise AttributeError(item)

基本上我想做的是将所有(总和)属性从“部分”传播到卫星中。即如果我有10个具有质量的部分,卫星的质量就是那些的总和。 - 如果我然后添加另一个有能量存储的部分 - 我立刻也能看到它。 - 如果没有任何部分具有该属性,则该属性被视为“不良”/“不存在”,并且会引发正常错误。

现在这样可行,除非我这样做:

s = satellite(None) #most simplistic one
ss = copy.copy(s) 

整个事情都是错误的,在__getattr__()中给出了无限递归深度错误。

现在检查(pycharm的调试器)告诉我它继续使用as参数迭代getattr:

item = _satellite__all_parts 它在for i in self.__all_parts:

行开始下一次迭代

现在我对此感到吃惊:为什么这条线甚至会转到__getattr_() - 据我所知__getattr__只调用不存在的属性? - 但self.__all_parts显然是在对象的__init__事件中声明的,那么__getattr__为什么会被激活呢?而且:为什么它不再理解这个对象了?

当然:我怎样才能做到这一点?

编辑:为了清楚起见 - 这只发生于复制,而且(由于martijn)特定于复制行为。链接的问题不处理复制案例。

1 个答案:

答案 0 :(得分:4)

copy.copy()尝试访问实例上的__setstate__属性,尚未设置任何属性,因为__init__尚未被调用(并赢得了' t be; copy.copy()负责在其上设置新属性。)

__setstate__ method copy.copy()正在寻找是可选的,而您的类确实没有这样的属性。由于缺少__getattr__,因为<{1}}属性 __all_parts行最终会调用for i in self.__all_parts:再次。一次又一次。

诀窍是通过测试来尽早切断这个循环。执行此操作的最佳方法是特殊情况下以下划线开头的所有属性:

__getattr__