How to override __getattr__
with python 3 and inheritance?
When I use the following:
class MixinA:
def __getattr__(self, item):
# Process item and return value if known
if item == 'a':
return 'MixinA'
# If it is unknown, pass it along to give
# a chance to another class to handle it
return super().__getattr__(item)
class MixinB:
def __getattr__(self, item):
# Process item and return value if known
if item == 'b':
return 'MixinB'
# If it is unknown, pass it along to give
# a chance to another class to handle it
return super().__getattr__(item)
class Example(MixinA, MixinB):
# main class
pass
I get this error.
>>> e = Example()
>>> e.a
'MixinA'
>>> e.b
'MixinB'
>>> e.c
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
...
AttributeError: 'super' object has no attribute '__getattr__'
Couldn't I just get the attribute error referencing the original class and property? That is to say:
AttributeError: 'Example' object has no attribute 'c'
PS: I found this post 'super' object not calling __getattr__ but I'm not sure to understand whether there is a solution.
The Python attribute retrieval mechanism works in a way that a class __getattr__
is called as "last resource" to try to get an attribute for an instance of that class.
Your code is right - it fails due to your superclass of "Example" - in this case "object" - not having a __getattr__
attribute.
If you are not at a deep class hierarchy and want to perform custom attribute retrieval for a class inheriting directly from object (which in Py3 can be expressed as having no bases at all as is the case in your code) - just raise "AttributeError" if your custom lookup failed.
If your intention is to, instead, make yur custom search have priority over Python's normal attribute retreival mechanism (and not be called as a fallback) you should implement __getattribute__
instead of __getattr__
.
In this case, the base class - object - does have a __getattribute__
method you have to call for ordinary attribute retrieval - the problem is that you have to call it for everything you want - including method names, and known attributes you had set. i.e.: something along:
class Example:
def __init__(self):
self.attrs_to_override = ["attr1", "foo", "bar"]
def docustomstuff(self, attr):
...
def __getattribute__(self, attr):
getter = super().__getattribute__
if attr in getter("attrs_to_override"):
getter("docustomstuff")(attr)
return getter(attr)
In short, if you think you should implement __getattribute__
instead of __getattr__
you probably are trying the wrong approach, except in very specific cases. What you probably didn't know up to here is that: __getattr__
would not have been called if an ordinary attribute by the wanted name did not already exist, so there is no need to call it in the superclass (unless the superclass in case is not object and has a known customization for it)
edit
Alternatively, just check if the next superclass do have __getattr__
in the most plain way:
...
if hasattr(super(), "__getattr__"):
return super().__getattr__(attr)
raise AttributeError