Python: 为什么__getattr__会捕获AttributeErrors?

2024-05-29 08:12:11 发布

您现在位置:Python中文网/ 问答频道 /正文

我在和__getattr__斗争。我有一个复杂的递归代码库,在这里让异常传播是很重要的。

class A(object):
    @property
    def a(self):
        raise AttributeError('lala')

    def __getattr__(self, name):     
        print('attr: ', name)
        return 1      

print(A().a)

结果:

('attr: ', 'a')
1

为什么会有这种行为?为什么没有抛出异常?这种行为没有记录(^{} documentation)。getattr()可以使用A.__dict__。有什么想法吗?


Tags: 代码nameselfreturnobjectdef记录property
3条回答

^{} documentation说:

If the class also defines __getattr__(), the latter will not be called unless __getattribute__() either calls it explicitly or raises an AttributeError.

我读到这篇文章(作者inclusio unius est exclusio alterius)时说,如果object.__getattribute__(即“无条件调用以实现属性访问”)碰巧引发AttributeError,则属性访问调用__getattr__;注意^{}应该“返回(计算的)属性值或引发AttributeError异常”。

作为类比,运算符特殊方法可以引发NotImplementedError,然后尝试其他运算符方法(例如__radd__用于__add__)。

在同一个类中使用__getattr__和属性是危险的,因为这会导致很难调试的错误。

如果属性的getter抛出AttributeError,那么AttributeError将被静默捕获,并调用__getattr__。通常,这会导致__getattr__失败并出现异常,但如果您非常不走运,则不会,甚至无法轻松地将问题追溯到__getattr__

除非您的属性getter很简单,否则您永远无法100%确定它不会抛出AttributeError。异常可能被抛出几个级别。

以下是您可以做的:

  1. 避免在同一个类中使用属性和__getattr__
  2. 向所有不重要的属性getter添加try ... except
  3. 保持属性getter简单,这样就知道它们不会抛出AttributeError
  4. 编写自己版本的@propertydecorator,它捕获AttributeError,并将其重新抛出为RuntimeError

另见http://blog.devork.be/2011/06/using-getattr-and-property_17.html

编辑:如果有人在考虑解决方案4(我不推荐),可以这样做:

def property_(f):
    def getter(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except AttributeError as e:
            raise RuntimeError, "Wrapped AttributeError: " + str(e), sys.exc_info()[2]

    return property(getter)

然后在重写__getattr__的类中使用@property_,而不是@property

我刚把密码改成

class A(object):
    @property
    def a(self):
        print "trying property..."
        raise AttributeError('lala')
    def __getattr__(self, name):     
        print('attr: ', name)
        return 1      

print(A().a)

而且,正如我们所看到的,事实上,这一财产是先受审判的。但由于它声称不存在(通过提高AttributeError),因此__getattr__()被称为“最后的手段”。

它没有明确的文档记录,但是可以在“当属性查找在通常的位置没有找到属性时调用”下计算。

相关问题 更多 >

    热门问题