Python:为什么__getattr__捕获AttributeErrors?

Dav*_*ter 13 python exception-handling exception python-2.7 python-3.x

我正在努力__getattr__.我有一个复杂的递归代码库,让异常传播很重要.

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

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

print(A().a)
Run Code Online (Sandbox Code Playgroud)

结果是:

('attr: ', 'a')
1
Run Code Online (Sandbox Code Playgroud)

为什么会这样?为什么没有例外?此行为未记录(__getattr__文档).getattr()可以使用A.__dict__.有什么想法吗?

glg*_*lgl 9

我只是将代码更改为

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)
Run Code Online (Sandbox Code Playgroud)

而且,正如我们所见,确实首先尝试了财产.但是,因为它声称不在那里(通过提高AttributeError),__getattr__()被称为"最后的手段".

它没有明确记录,但可以计入"当属性查找没有在通常的位置找到属性时调用".

  • @glglgl:这不像是我引发了一个`AttributeError`,我只是犯了编程错误(访问一个不存在的属性).最后我什么都没看到,因为它被`__getattr__`抓住了. (3认同)
  • 我会说不会引发`AttributeError`,或者在`__getattr__`中处理这种情况,或者使用`__getattribute__`并调用`super(...).__ getattribute__`,捕获`AttributeError`. (2认同)

Flo*_*ter 8

__getattr__在同一个类中使用和属性是危险的,因为它可能导致很难调试的错误.

如果属性的getter抛出AttributeError,则会AttributeError被静默捕获,并被__getattr__调用.通常情况下,这会导致__getattr__异常失败,但如果您非常不走运,则不会,并且您甚至无法轻松追溯问题__getattr__.

除非你的财产获取者是微不足道的,否则你永远不能100%确定它不会抛出AttributeError.异常可能会被抛出几个层次.

这是你可以做的:

  1. 避免__getattr__在同一个类中使用属性.
  2. try ... except块添加到所有非常重要的属性getter
  3. 保持属性getter简单,所以你知道他们不会抛出 AttributeError
  4. 编写自己的@property装饰器版本,将其捕获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)
Run Code Online (Sandbox Code Playgroud)

然后使用@property_而不是@property覆盖的类__getattr__.


eca*_*mur 6

__getattribute__文件说:

如果类也定义__getattr__(),除非__getattribute__()明确调用它或引发一个,否则不会调用后者AttributeError.

我读到这个(通过inclusio unius est exclusio alterius)说,属性访问调用__getattr__if object.__getattribute__(" 无条件地实现属性访问 ")恰好引发AttributeError- 无论是直接还是在描述符内部__get__(例如属性fget); 请注意__get__应该" 返回(计算的)属性值或引发AttributeError异常 ".

作为类比,操作员特殊方法可以提出NotImplementedError其他操作员方法(例如__radd__for __add__)将被尝试.