'super' object has no attribute '__getattr__' in python3
Solution 1
If you want to do things with mixin-style classes then you need to create a base-attribute-getting-class that always raises an AttributeError
.
class AttrGetter:
def __getattr__(self, item):
raise AttributeError(item)
class MixinA(AttrGetter):
def __getattr__(self, item):
if item == 'a':
return 'MixinA'
return super().__getattr__(item)
class MixinB(AttrGetter):
def __getattr__(self, item):
if item == 'b':
return 'MixinB'
return super().__getattr__(item)
class Example(MixinA, MixinB):
pass
# mro stands for method resolution order.
# It defines the order in which classes are searched for attributes.
# We're looking for __getattr__ in this circumstance.
print(Example.mro())
# [<class '__main__.Example'>, <class '__main__.MixinA'>, <class '__main__.MixinB'>,
# <class '__main__.AttrGetter'>, <class 'object'>]
# As you can see, searches for __getattr__ only check AttrGetter after having checked
# both mixins first.
e = Example()
print(e.a)
print(e.b)
print(e.c)
However, properties allow you to do this in much easier and more succinct way.
class MixinA:
@property
def a(self):
return "MixinA"
class MixinB:
@property
def b(self):
return "MixinB"
class Example(MixinA, MixinB):
pass
e = Example()
print(e.a)
print(e.b)
print(e.c)
Solution 2
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
Michael
Updated on March 05, 2020Comments
-
Michael about 4 years
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.
-
Michael about 9 yearsThank you for your response. Yes I think I understand the difference between
__getattr__
and__getattribute__
and I mean to use__getattr__
. I do not want to raiseAttributeError
because I want to have several mixins implementing the__getattr__
method and I want them all to get a chance to intercept it. I edited my post to show this example. Let me know what you think. Basically I already get theAttributeError
but I would like it to be on the high level class and property instead on thesuper
. Any idea? -
jsbueno about 9 yearsThere - I added an example of how to do it with
__getattr__