Fixing 'super' object has no attribute '__getattr__' in Python 3: How to Override __getattr__ with Inheritance

Python’s __getattr__ method is a powerful tool for dynamic attribute access, allowing classes to define custom behavior when an attribute is not found. However, when using inheritance and super() to delegate attribute lookups to parent classes, developers often encounter the cryptic error: AttributeError: 'super' object has no attribute '__getattr__'.

This blog demystifies this error, explains why it occurs, and provides actionable solutions to fix it. Whether you’re a beginner or an experienced developer, understanding how __getattr__ interacts with inheritance and super() is critical for writing robust, maintainable code.

Table of Contents#

  1. Understanding __getattr__ and __getattribute__
  2. The 'super' Object Error Explained
  3. Common Scenarios Causing the Error
  4. Step-by-Step Fixes
  5. Best Practices
  6. Conclusion
  7. References

Understanding __getattr__ and __getattribute__#

Before diving into the error, it’s essential to distinguish between __getattr__ and __getattribute__—two special methods in Python’s data model that control attribute access.

What is __getattr__?#

__getattr__ is a fallback method: it is only called when an attribute lookup fails to find the attribute in the usual places (i.e., the instance’s __dict__, class, or parent classes). Its signature is:

def __getattr__(self, name: str) -> Any:  
    ...  

If __getattr__ is defined, and the attribute name is not found, Python invokes this method to handle the lookup.

What is __getattribute__?#

__getattribute__ is a primary method: it is called for every attribute lookup, regardless of whether the attribute exists. Its signature is:

def __getattribute__(self, name: str) -> Any:  
    ...  

Overriding __getattribute__ is risky because it can break normal attribute access (e.g., if you forget to call super().__getattribute__(name)).

Key Difference#

  • __getattr__: Fallback for missing attributes (called after normal lookup fails).
  • __getattribute__: Primary handler for all attributes (called before normal lookup).

The error we’re addressing involves __getattr__, not __getattribute__, so we’ll focus on __getattr__ here.

The 'super' Object Error Explained#

The error 'super' object has no attribute '__getattr__' occurs when a child class’s __getattr__ method tries to delegate to a parent class (via super()) that does not define __getattr__.

Why Does This Happen?#

super() returns a "proxy object" that delegates method calls to the next class in the Method Resolution Order (MRO). If the next class in the MRO does not have a __getattr__ method, super().__getattr__(name) will fail with an AttributeError.

Example Code Triggering the Error#

Let’s define a parent class without __getattr__ and a child class that tries to delegate __getattr__ to the parent:

class Parent:  
    pass  # Parent has no __getattr__ method  
 
class Child(Parent):  
    def __getattr__(self, name):  
        # Delegate to parent's __getattr__ (which doesn't exist)  
        return super().__getattr__(name)  
 
# Attempt to access a missing attribute  
child = Child()  
print(child.missing_attr)  # Triggers the error  

Error Traceback#

Running this code produces:

AttributeError: 'super' object has no attribute '__getattr__'  

Why? The Child class’s __getattr__ calls super().__getattr__(name), but Parent (the next class in the MRO) has no __getattr__ method. Since Parent inherits from object (Python’s base class), and object also lacks __getattr__, the proxy object returned by super() cannot find __getattr__.

Common Scenarios Causing the Error#

The error typically arises in three scenarios:

1. Inheriting from a Parent Without __getattr__#

If the immediate parent class (or any class in the MRO) does not define __getattr__, calling super().__getattr__() in the child will fail.

2. Inheriting from object Directly#

object (Python’s root class) does not define __getattr__. Thus, any class inheriting directly from object and calling super().__getattr__() will trigger the error:

class MyClass(object):  
    def __getattr__(self, name):  
        return super().__getattr__(name)  # Fails: object has no __getattr__  
 
MyClass().missing_attr  # AttributeError  

3. Multiple Inheritance Confusion#

With multiple inheritance, the MRO determines the order in which parent classes are checked. If none of the classes in the MRO define __getattr__, super().__getattr__() will fail:

class A:  
    pass  
 
class B:  
    pass  
 
class C(A, B):  # MRO: C -> A -> B -> object  
    def __getattr__(self, name):  
        return super().__getattr__(name)  # A, B, and object have no __getattr__  
 
C().missing_attr  # AttributeError  

Step-by-Step Fixes#

To resolve the error, we need to ensure that super().__getattr__() only calls a parent class that actually defines __getattr__. Below are actionable fixes:

Fix 1: Remove Unnecessary super() Calls#

If the parent class (or MRO) has no __getattr__, the child’s __getattr__ should handle the lookup directly instead of delegating.

Example:

class Parent:  
    pass  
 
class Child(Parent):  
    def __getattr__(self, name):  
        # Handle missing attributes directly (no super() call)  
        if name == "dynamic_attr":  
            return "I'm dynamic!"  
        # Raise AttributeError for unhandled attributes  
        raise AttributeError(f"'Child' object has no attribute '{name}'")  
 
child = Child()  
print(child.dynamic_attr)  # Output: "I'm dynamic!"  
print(child.missing_attr)  # Raises AttributeError (correct behavior)  

Why this works: By removing the super() call, we avoid delegating to a parent that lacks __getattr__. The child handles the lookup, and raises AttributeError for unrecognized attributes (mimicking Python’s default behavior).

Fix 2: Define __getattr__ in the Parent Class#

If the parent should participate in attribute lookup, define __getattr__ in the parent to enable delegation.

Example:

class Parent:  
    def __getattr__(self, name):  
        # Parent's fallback behavior  
        if name == "parent_attr":  
            return "From Parent!"  
        raise AttributeError(f"'Parent' object has no attribute '{name}'")  
 
class Child(Parent):  
    def __getattr__(self, name):  
        try:  
            # Delegate to Parent's __getattr__ first  
            return super().__getattr__(name)  
        except AttributeError:  
            # Handle child-specific attributes if Parent fails  
            if name == "child_attr":  
                return "From Child!"  
            raise  # Re-raise if neither Parent nor Child handles it  
 
child = Child()  
print(child.parent_attr)  # Output: "From Parent!" (delegated to Parent)  
print(child.child_attr)   # Output: "From Child!" (handled by Child after Parent fails)  
print(child.missing_attr) # Raises AttributeError (correct)  

Why this works: Parent now has __getattr__, so super().__getattr__(name) in Child successfully delegates to Parent.

Fix 3: Conditionally Call super().__getattr__#

Use hasattr() to check if the super object has __getattr__ before calling it. This avoids errors in cases where the MRO may or may not include a class with __getattr__.

Example:

class Parent:  
    pass  
 
class Child(Parent):  
    def __getattr__(self, name):  
        # Check if super() has __getattr__ before calling  
        if hasattr(super(), "__getattr__"):  
            return super().__getattr__(name)  
        # Handle missing attributes if no parent __getattr__  
        raise AttributeError(f"'Child' object has no attribute '{name}'")  
 
child = Child()  
child.missing_attr  # Raises AttributeError (no parent __getattr__, so condition fails)  

Why this works: hasattr(super(), "__getattr__") safely checks if the next class in the MRO defines __getattr__ before invoking it.

Fix 4: Use object as a Last Resort#

If all else fails, explicitly delegate to object (Python’s base class) after handling child-specific logic. Since object has no __getattr__, this will raise AttributeError, which is the correct behavior for missing attributes.

Example:

class Child:  
    def __getattr__(self, name):  
        if name == "custom_attr":  
            return "Custom value"  
        # Delegate to object (raises AttributeError for missing attributes)  
        return object.__getattribute__(self, name)  
 
Child().custom_attr  # Output: "Custom value"  
Child().missing_attr # Raises AttributeError (correct)  

Why this works: object.__getattribute__ is the default attribute lookup method. If the attribute isn’t found, it raises AttributeError, which aligns with Python’s expected behavior.

Best Practices#

To avoid the super error and write robust __getattr__ methods, follow these best practices:

1. Understand the MRO#

Always check the Method Resolution Order (MRO) of your class to know which parent classes super() will delegate to. Use ClassName.__mro__ to inspect the MRO:

print(Child.__mro__)  # Output: (<class '__main__.Child'>, <class '__main__.Parent'>, <class 'object'>)  

2. Avoid Overusing super() in __getattr__#

Only call super().__getattr__() if you’re certain a parent class in the MRO defines __getattr__. Unnecessary delegation is a common source of errors.

3. Raise AttributeError for Unhandled Attributes#

__getattr__ should raise AttributeError for attributes it cannot handle. This ensures Python’s default error messages are preserved (e.g., 'Child' object has no attribute 'x').

4. Distinguish __getattr__ and __getattribute__#

Never confuse __getattr__ (fallback for missing attributes) with __getattribute__ (handles all attributes). Overriding __getattribute__ requires careful use of super().__getattribute__(name) to avoid breaking normal access.

Conclusion#

The 'super' object has no attribute '__getattr__' error occurs when a child class’s __getattr__ delegates to a parent class that lacks __getattr__. By understanding the MRO, avoiding unnecessary super() calls, and ensuring parents define __getattr__ when needed, you can resolve this error and write clean, maintainable inheritance hierarchies.

Remember: __getattr__ is a fallback—use it to handle dynamic attributes, and delegate to parents only if they explicitly support the method.

References#