How do I override the __getattr__
method of a class without breaking the default behavior?
How do I override __getattr__ 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)
Bah, your edit has the same thing I was showing in my answer, +1. –
Namhoi
Cool, Python doesn't seem to like calling super's in
__getattr__
-- any ideas what to do? (AttributeError: 'super' object has no attribute '__getattr__'
) –
Absorptivity Without seeing your code it's hard to tell, but that looks like none of your superclasses define getattr. –
Pavis
This works with hasattr also because: "This is implemented by calling getattr(object, name) and seeing whether it raises an exception or not." docs.python.org/2/library/functions.html#hasattr –
Secretarial
-1 This does modify default behavior. Now you have an
AttributeError
without the context of the attribute in the exception args. –
Stegosaur @Stegosaur what to do then? –
Seismology
@Seismology Either of the other 2 answers show how to keep context here. Unfortunately O.P. accepted the bad answer, and it got stuck to the top all those years ago.. –
Stegosaur
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
Thanks for this. Just wanted to make sure I had the default message correct without digging around in the source. –
Henriques
I think that
self.__class__.__name__
should be used instead of self.__class__
in case the class overrides __repr__
–
Psalms This is better than accepted answer, but it would be nice not to have to rewrite that code and potentially miss the upstream changes should the wording be modified or extra context added to the exception object in future. –
Stegosaur
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'
@fed.pavlo are you sure? Maybe you mixed
__getattr__
and __getattribute__
? –
Insnare my bad. I missed call to different method from self. ;( –
Krell
Indeed @Michael's answer is incomplete without this answer –
Thinner
Shouldn't it be
return super().__getattribute__(name)
just in case you also have a __getattribute__
method in this class. –
Spindle @Spindle Yes, you might be right. Otherwise the
Foo.__getattribute__
would be invoked multiple times, unnecessarily –
Stegosaur © 2022 - 2024 — McMap. All rights reserved.