How do I override __getattr__ in Python without breaking the default behavior?

后端 未结 3 1658
走了就别回头了
走了就别回头了 2020-11-29 15:56

I want to override the __getattr__ method on a class to do something fancy but I don\'t want to break the default behavior.

What\'s the correct way to d

相关标签:
3条回答
  • 2020-11-29 16:15
    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
    
    0 讨论(0)
  • 2020-11-29 16:17

    Overriding __getattr__ should be fine -- __getattr__ is only called as a last resort i.e. if there are no attributes in the instance that match the name. For instance, if you access foo.bar, then __getattr__ will only be called if foo has no attribute called bar. If the attribute is one you don't want to handle, raise AttributeError:

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

    However, unlike __getattr__, __getattribute__ will be called first (only works for new style classes i.e. those that inherit from object). In this case, you can preserve default behaviour like so:

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

    See the Python docs for more.

    0 讨论(0)
  • 2020-11-29 16:23

    To extend Michael answer, if you want to maintain the default behavior using __getattr__, you can do it like so:

    class Foo(object):
        def __getattr__(self, name):
            if name == 'something':
                return 42
    
            # Default behaviour
            return self.__getattribute__(name)
    

    Now the exception message is more descriptive:

    >>> 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'
    
    0 讨论(0)
提交回复
热议问题