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

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


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.


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.a42>>> a.b42>>> a.missingTraceback (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


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.something42>>> foo.errorTraceback (most recent call last):  File "<stdin>", line 1, in <module>  File "<stdin>", line 5, in __getattr__AttributeError: 'Foo' object has no attribute 'error'