如何防止未声明的变量和方法的AttributeError并多次修复__getattr__方法调用?

时间:2017-07-21 09:55:53

标签: python python-2.x

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。

我想要实现的是,如果类中没有声明类变量或方法,那么有一个类不会引发错误。

可能的原因是什么?

4 个答案:

答案 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__