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

Infinite recursion for __getattr__() - but why is it even called once?

我有一个 class 包括(相关部分):

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)

基本上我想做的是将 "parts" 的所有(总和)属性传播到卫星中。也就是说,如果我有 10 个有质量的部分,卫星的质量就是这些部分的总和。 - 如果我再添加另一个具有能量存储的部分 - 我也可以立即查看。 - 如果没有部件具有该属性,则该属性被认为是 "bad"/"unexistent" 并引发正常错误。

现在可以了,除非我这样做了:

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

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

现在检查(pycharm 的调试器)告诉我它不断迭代 getattr 作为参数:

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

行开始下一次迭代

现在我被这一点吓了一跳:为什么这条线甚至会转到 __getattr_() - 据我所知 __getattr__ 只为不存在的属性调用? - 但是self.__all_parts明明是在对象的__init__事件中声明的,那么为什么__getattr__还要被激活呢?此外:为什么它不再理解该对象?

当然还有:我怎样才能让它发挥作用?

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

copy.copy() 尝试访问 实例上的 __setstate__ 属性,尚未设置属性,因为 __init__ 尚未被调用(并且不会被调用;copy.copy() 负责在其上设置新属性)。

找的__setstate__ methodcopy.copy()是可选的,而你的class确实没有这个属性。因为它丢失了,所以调用 __getattr__,并且因为还没有 __all_parts 属性 或者 for i in self.__all_parts: 行最终调用 __getattr__ 再一次。一次又一次。

诀窍是通过测试尽早切断这个循环。最好的方法是 special-case 所有以下划线开头的属性:

if item.startswith('_'):
    # bail out early
    raise AttributeError(item)