如何在不破坏默认行为的情况下在Python中重写__getattr__?


185

我想重写__getattr__类上的方法以执行某些操作,但我不想破坏默认行为。

正确的方法是什么?


20
它要求的是“断裂”,而不是“改变”。这已经足够清楚了:“ fancy”属性不应干扰内置属性,并且应像它们一样表现得尽可能多。迈克尔的答案既正确又有用。
olooney 2010年

Answers:


268

覆盖__getattr__应该没问题- __getattr__仅可作为最后的选择,即,如果实例中没有与名称匹配的属性。例如,如果您访问foo.bar,则__getattr__仅当foo没有调用属性时才会被调用bar。如果该属性是您不想处理的属性,请引发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)

有关更多信息,请参见Python文档


ah,您所做的修改与我在答案中显示的内容相同+1。

12
太酷了,Python似乎不喜欢在其中调用super- __getattr__任何想法该怎么做?(AttributeError: 'super' object has no attribute '__getattr__'
gatoatigrado

1
没有看到您的代码很难说,但是看起来您的超类都没有定义getattr
Colin vH 2015年

这也适用于hasattr,还因为:“这是通过调用getattr(object,name)并查看它是否引发异常来实现的。” docs.python.org/2/library/functions.html#hasattr
ShaBANG

1
-1这确实会修改默认行为。现在,您AttributeError在异常args中有了一个没有属性上下文的。
WIM

33
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

谢谢你 只是想确保我的默认消息正确无误,而无需深入了解源代码。
ShawnFumo

4
我认为self.__class__.__name__应该使用self.__class__该类,以防万一该类被覆盖__repr__
Michael Scott Cuthbert

3
这比接受的答案要好,但是最好不要重写该代码,如果将来修改措辞或在异常对象中添加额外的上下文,则可能会错过上游更改。
wim

12

为了扩展Michael的答案,如果您想使用来维持默认行为__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'

2
您确定@ fed.pavlo吗?也许你__getattr____getattribute__
何塞·路易斯

我的错。我错过了打电话给自己的另一种方法。;(
fed.pavlo 16/09/19

1
的确,如果没有这个答案,@ Michael的答案是不完整的
Grijesh Chauhan
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.