class Foo(object):
def __init__(self):
self.a = 1
self.c = 0
def __getattr__(self, name):
self.c += 1
print('getattribute')
if hasattr(self, name) is False:
print("No")
return None
return super(Foo, self).__getattr__(self, name)
fo = Foo()
print(fo.a)
print(fo.b)
print(fo.c)
运行上述程序打印" getattribute"和"不"多次。多次调用__getattr__
。确切地说是333。 self.c
打印333。
我想要实现的是,如果类中没有声明类变量或方法,那么有一个类不会引发错误。
可能的原因是什么?
答案 0 :(得分:6)
hasattr
只是尝试获取属性,如果不能,则返回False
。是否不能通过尝试抛出AttributeError
在Python 3中确定,而在Python 2中通过尝试抛出任何错误来确定。 (这包括RecursionError
,这就是它在333次调用后无声地失败的原因.Python 2不是一种理智的语言;如果可能的话,升级到3级。)
相反,您可以自己在AttributeError
上返回替代值:
def __getattr__(self, name):
try:
return super(Foo, self).__getattr__(self, name)
except AttributeError:
return None
这可能隐藏其他AttributeError
,但很难避免仅仅因为Python的性质而这样做。
答案 1 :(得分:3)
hasattr是一个调用getattr的快捷方式,看看它是否引发异常(这意味着属性不存在)或不是(这意味着它存在)
cf:https://docs.python.org/3/library/functions.html#hasattr
getattr调用__getattr__,因此您正在进行递归调用
我认为可能的解决方法是使用:
name in self.__dict__
而不是:
hasattr(self, name)
答案 2 :(得分:1)
这是因为正在hasattr(self, name)
拨打self.__getattr__(name)
(又名getattr(self, name)
) - reference。
因此,当hasattr(self, name)
内的__getattr__
调用self.__getattr__(name)
时,会发出不需要的递归。
我会解决它:
class Foo(object):
def __init__(self):
self.a = 1
self.c = 0
def __getattr__(self, name):
self.__dict__['c'] += 1
print('getattribute')
try:
return self.__dict__[name]
except KeyError:
print('No')
return None
fo = Foo()
print(fo.a)
print(fo.b)
print(fo.c)
答案 3 :(得分:0)
问题来自print(fo.b)
。
由于b
未定义为Foo
的成员,fo.b
会调用fo.__getattr__('b')
。
然后,hasattr(self, name)
,相当于hasattr(fo, 'b')
自称gettatr(fo, 'b')
,如hte documentation中所述。
因此无限递归,导致Python 3中出现RecursionError
。
如果您知道fo.b
没有Foo
成员,那么获取b
并不合理,我建议的第一个修正是定义该成员。
class Foo(object):
def __init__(self):
...
self.b = 1
...
您的代码然后输出
1
1
0
更聪明的解决方法是检查传递给name
的{{1}}参数是__getattr__
,还是根据您的需要,与'b'
和{{1}不同}}。
在这种情况下,您可以强制'a'
定义请求的未发送成员。
'c'
可替换地:
__getattr__