__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)
我有一个 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)