'super' object has no attribute '__getattr__' in python3

Michael picture Michael · Mar 4, 2015 · Viewed 15.6k times · Source

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.

Answer

jsbueno picture jsbueno · Mar 4, 2015

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