How do I override __getattr__ in Python without breaking the default behavior?
PythonGetattrGetpropertyPython Problem Overview
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 do this?
Python Solutions
Solution 1 - Python
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)
Solution 2 - Python
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
Solution 3 - Python
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'