Python
getattr
getattribute
magic methods
Python programming

Difference between __getattr__ and __getattribute__

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

__getattr__ and __getattribute__ both participate in attribute access, but they are not interchangeable. The short version is that __getattribute__ runs for every attribute lookup, while __getattr__ runs only as a fallback when normal lookup fails.

How Normal Attribute Lookup Works

When Python evaluates obj.name, it normally searches in this general order:

  • instance attributes
  • class attributes and descriptors
  • inherited attributes

If an attribute is found through the normal machinery, __getattr__ is not called. __getattribute__, however, sits at the front of the process.

__getattribute__: Intercept Every Lookup

If a class defines __getattribute__, Python calls it for every attribute access on the instance.

python
1class Verbose:
2    def __init__(self):
3        self.value = 42
4
5    def __getattribute__(self, name):
6        print(f"Looking up {name}")
7        return object.__getattribute__(self, name)
8
9
10obj = Verbose()
11print(obj.value)

Output:

text
Looking up value
42

This is powerful because you can log, proxy, validate, or compute attributes centrally. It is also dangerous because a careless implementation can recurse forever.

__getattr__: Fallback for Missing Attributes

__getattr__ is called only when the attribute was not found by the normal process.

python
1class Defaults:
2    def __init__(self):
3        self.language = "Python"
4
5    def __getattr__(self, name):
6        if name == "version":
7            return "3.x"
8        raise AttributeError(name)
9
10
11obj = Defaults()
12print(obj.language)
13print(obj.version)

Here:

  • 'obj.language is found normally, so __getattr__ is skipped'
  • 'obj.version is missing, so __getattr__ supplies a value'

This makes __getattr__ a good tool for defaults, lazy loading, compatibility aliases, and delegated access.

The Biggest Practical Difference

Think of the methods this way:

  • '__getattribute__: "I want to control all attribute access."'
  • '__getattr__: "I only care about attributes that do not already exist."'

That difference affects both behavior and maintenance cost. In many cases, __getattr__ is safer because it leaves standard attribute access alone.

Avoiding Infinite Recursion

Inside __getattribute__, you must not access attributes through self.name unless you deliberately want another lookup cycle. Use object.__getattribute__ instead.

Broken version:

python
class Broken:
    def __getattribute__(self, name):
        return self.__dict__[name]

This tries to read self.__dict__, which triggers __getattribute__ again, causing recursion.

Correct version:

python
1class Safe:
2    def __init__(self):
3        self.value = 10
4
5    def __getattribute__(self, name):
6        data = object.__getattribute__(self, "__dict__")
7        if name in data:
8            return data[name]
9        return object.__getattribute__(self, name)

In __getattr__, recursion is less common because the method runs only after normal lookup fails, but you still need to raise AttributeError for truly missing names. Returning None silently often breaks tools like hasattr and introspection.

Typical Use Cases

Use __getattr__ when:

  • you want computed defaults for missing fields
  • you are forwarding unknown names to another object
  • you are implementing lazy attributes that appear on first access

Use __getattribute__ when:

  • you are building a proxy object
  • you need to audit every lookup
  • you want uniform access control over all attributes

If you do not need that much control, prefer __getattr__. It is easier to reason about and far less likely to interfere with normal object behavior.

Common Pitfalls

The most common mistake is implementing __getattribute__ and then using regular attribute access inside it. That almost always causes infinite recursion unless you route through object.__getattribute__.

Another mistake is forgetting to raise AttributeError in __getattr__ for names you cannot handle. Python expects that exception to mean "attribute really is missing."

A third issue is overusing __getattribute__ when a property, descriptor, or __getattr__ would be enough. Full interception adds complexity and can make debugging awkward.

Finally, people sometimes assume __getattr__ runs for every attribute. It does not. If the attribute exists normally, Python never calls it.

Summary

  • '__getattribute__ runs for every attribute access.'
  • '__getattr__ runs only when normal lookup fails.'
  • Use object.__getattribute__ inside __getattribute__ to avoid recursion.
  • Use __getattr__ for missing-attribute defaults and delegation.
  • Prefer the simpler hook unless you truly need total control over lookup behavior.

Course illustration
Course illustration

All Rights Reserved.