【问题标题】:How do I override __getattr__ in Python without breaking the default behavior?如何在不破坏默认行为的情况下覆盖 Python 中的 __getattr__?
【发布时间】:2010-03-08 23:29:32
【问题描述】:

我想重写一个类上的__getattr__ 方法来做一些花哨的事情,但我不想破坏默认行为。

这样做的正确方法是什么?

【问题讨论】:

  • “中断”,而不是“改变”。这很清楚:“花式”属性不应该干扰内置属性,并且应该尽可能地像它们一样表现。迈克尔的回答既正确又有帮助。

标签: python getattr getproperty


【解决方案1】:

覆盖__getattr__ 应该没问题——__getattr__ 仅作为最后的手段调用,即如果实例中没有与名称匹配的属性。例如,如果您访问foo.bar,那么只有在foo 没有名为bar 的属性时才会调用__getattr__。如果该属性是您不想处理的属性,请提出AttributeError

class Foo(object):
    def __getattr__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            raise AttributeError

但是,与__getattr__ 不同,__getattribute__ 将首先被调用(仅适用于新样式类,即那些从对象继承的类)。在这种情况下,您可以像这样保留默认行为:

class Foo(object):
    def __getattribute__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            return object.__getattribute__(self, name)

the Python docs for more

【讨论】:

  • 呸,您的编辑与我在答案中显示的内容相同,+1。
  • 酷,Python 似乎不喜欢在 __getattr__ 中调用 super —— 知道该怎么做吗? (AttributeError: 'super' object has no attribute '__getattr__')
  • 没有看到你的代码很难说,但看起来你的超类都没有定义 getattr
  • 这也适用于 hasattr,因为:“这是通过调用 getattr(object, name) 并查看它是否引发异常来实现的。” docs.python.org/2/library/functions.html#hasattr
  • -1 这确实修改默认行为。现在你有一个 AttributeError 没有异常 args 中属性的上下文。
【解决方案2】:
class A(object):
    def __init__(self):
        self.a = 42

    def __getattr__(self, attr):
        if attr in ["b", "c"]:
            return 42
        raise AttributeError("%r object has no attribute %r" %
                             (self.__class__.__name__, attr))

>>> a = A()
>>> a.a
42
>>> a.b
42
>>> a.missing
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in __getattr__
AttributeError: 'A' object has no attribute 'missing'
>>> hasattr(a, "b")
True
>>> hasattr(a, "missing")
False

【讨论】:

  • 谢谢。只是想确保我的默认消息正确无误,而无需深入研究源代码。
  • 我认为应该使用self.__class__.__name__ 而不是self.__class__,以防类覆盖__repr__
  • 这比接受的答案要好,但是如果将来修改措辞或向异常对象添加额外的上下文,不必重写该代码并可能错过上游更改会很好。
【解决方案3】:

为了扩展迈克尔的回答,如果你想使用__getattr__ 保持默认行为,你可以这样做:

class Foo(object):
    def __getattr__(self, name):
        if name == 'something':
            return 42

        # Default behaviour
        return self.__getattribute__(name)

现在异常消息更具描述性:

>>> foo.something
42
>>> foo.error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __getattr__
AttributeError: 'Foo' object has no attribute 'error'

【讨论】:

  • @fed.pavlo 你确定吗?也许你混合了__getattr____getattribute__
  • 我的错。我错过了与自己不同的方法的电话。 ;(
  • 如果没有这个答案,@Michael 的答案确实是不完整的
  • 不应该是return super().__getattribute__(name) ,以防万一你在这个类中也有__getattribute__方法。
猜你喜欢
  • 1970-01-01
  • 2014-12-12
  • 2016-05-17
  • 1970-01-01
  • 1970-01-01
  • 2013-01-02
  • 2018-10-07
  • 2019-11-16
  • 1970-01-01
相关资源
最近更新 更多