DEV Community

TildAlice
TildAlice

Posted on • Originally published at tildalice.io

__getattr__ vs __getattribute__: 3 Interception Patterns

The Silent Failure That Took 20 Minutes to Debug

A lazy-loading config object was silently returning None for every attribute that didn't exist. No errors, no warnings — just quiet failure that propagated through the entire pipeline until a JSON serialization crash three layers deep revealed the problem.

The culprit? Confusing __getattr__ with __getattribute__.

Python gives you two hooks for intercepting attribute access, and they behave radically differently. One only fires when normal lookup fails. The other intercepts every single access, including methods you didn't even know your class was using. Mix them up and you get subtle bugs that only surface in production.

Here's what happens when you run this:

class LazyConfig:
    def __getattribute__(self, name):
        print(f"__getattribute__: {name}")
        return object.__getattribute__(self, name)

    def __getattr__(self, name):
        print(f"__getattr__: {name}")
        return None

config = LazyConfig()
config.database_url  # What prints?
Enter fullscreen mode Exit fullscreen mode

Most developers expect __getattr__ to fire. But both methods print — and in a specific order that reveals Python's attribute lookup chain.


Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.

Continue reading the full article on TildAlice

Top comments (0)