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

20,935

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
Share:
20,935
Michael
Author by

Michael

Updated on March 05, 2020

Comments

  • Michael
    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
    Michael about 9 years
    Thank 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 raise AttributeError 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 the AttributeError but I would like it to be on the high level class and property instead on the super. Any idea?
  • jsbueno
    jsbueno about 9 years
    There - I added an example of how to do it with __getattr__