【问题标题】:Property and __getattr__ compatibility issue with AttributeErrorAttributeError 的属性和 __getattr__ 兼容性问题
【发布时间】:2014-09-26 17:32:54
【问题描述】:

我刚刚遇到了意外的行为。这是一个简单的类,其中包含一个 __getattr__ 方法和一个带有错字的属性:

class A(object):
    def __getattr__(self, attr):
        if not attr.startswith("ignore_"):
            raise AttributeError(attr)

    @property
    def prop(self):
        return self.some_typo

a = A() # Instantiating
a.ignore_this # This is ignored
a.prop # This raises an Attribute Error

这是预期的结果(如果__getattr__ 被评论,我得到的结果):

AttributeError: 'A' object has no attribute 'some_typo'

这就是我得到的:

AttributeError: prop

我知道这与__getattr__ 捕获AttributeError 有关,但是对于这个问题有没有一个好的和干净的解决方法?因为我可以向你保证,这是一场调试噩梦......

【问题讨论】:

  • 你的意思是真的“我怎样才能raise更好的错误信息”?另外,请记住a.prop 调用a.__getattr__('prop'),它(如果它没有引发错误)会依次调用a.__getattr__('some_typo') - 你希望错误来自哪里?
  • 更多的是不引发错误的错误信息。
  • 为什么你认为错误信息是错误的?调用__getattr__ 来解析prop
  • 为什么要调用 getattr 来解析@property?

标签: python properties


【解决方案1】:

您可以提出更好的异常消息:

class A(object):
  def __getattr__(self, attr):
    if not attr.startswith("ignore_"):
      raise AttributeError("%r object has not attribute %r" % (self.__class__.__name__, attr))

  @property
  def prop(self):
    return self.some_typo

a=A()
a.ignore_this
a.prop

编辑:从对象基类调用__getattribute__ 可以解决问题

class A(object):
  def __getattr__(self, attr):
    if not attr.startswith("ignore_"):
      return self.__getattribute__(attr)

  @property
  def prop(self):
    return self.some_typo

【讨论】:

  • 这不是关于消息,而是关于错字检测。你的代码引发AttributeError: 'A' object has not attribute 'prop',我需要它引发AttributeError: 'A' object has no attribute 'some_typo'
  • 很好的答案!我考虑过覆盖__getattribute__,但我不喜欢每次访问某些东西时都调用它的想法(性能问题)。这绝对是最好的方法!
  • 答案的第二部分极大地帮助我解决了__getattr__()@property之间的兼容性问题。值得注意的是,调用getattr(self, attr) 会导致无限递归。
  • 此修复不正确。按照它的编写方式,每个属性方法都会被调用两次(尝试将print('calling prop') 放入prop 方法中)。正确的解决方法是将__getattr__替换__getattribute__
【解决方案2】:

正如@asmeurer 所述,@mguijarr 的解决方案调用了两次prop。当prop 第一次运行时,它会引发一个触发__getattr__ 的AttributeError。然后self.__getattribute__(attr)再次触发prop,最终产生了想要的异常。

更好的答案

在这里,我们最好替换 __getattribute__ 而不是 __getattr__。它给了我们更多的控制权,因为__getattribute__ 在所有属性访问中都被调用。相比之下,__getattr__ 仅在已经存在 AttributeError 时才被调用,并且它不允许我们访问那个原始错误。

class A(object):
    def __getattribute__(self, attr):
        try:
            return super().__getattribute__(attr)
        except AttributeError as e:
            if not attr.startswith("ignore_"):
                raise e

    @property
    def prop(self):
        print("hi")
        return self.some_typo

解释一下,由于在这种情况下Aobject 的子类,所以super().__getattribute__(attr) 等价于object.__getattribute__(self, attr)。读取a 的底层object 属性,如果我们使用self.__getattribute__(attr) 来避免无限递归。

AttributeError 的情况下,我们可以完全控制失败或重新引发,并且重新引发会给出合理的错误消息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-12-11
    • 2013-01-25
    • 2011-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多