Python

Python __getattr__ for Missing Attributes

Learn how Python's __getattr__ method lets you handle missing attributes gracefully, from fallback defaults to proxy objects. Practical examples show when and how to use this powerful feature to write cleaner, more intuitive code.

August 2026 6 min read 12 views 0 hearts

Here is the article.


Python's __getattr__: How to Handle Missing Attributes Like a Pro

Ever had a moment where you wish an object could just figure things out on its own? Maybe you want to call user.email and have it automatically look up the domain, or you want to access a setting like config.SECRET_KEY but have it return a default value if it's not there.

In Python, you can do exactly that. It's not magic—it's a special method called __getattr__. It's one of those tools that feels a little advanced at first, but once you understand it, you'll start seeing places to use it everywhere.

What exactly is __getattr__?

Think of it as a safety net. When you try to access an attribute on an object (like my_object.some_value), Python first looks in the usual places: the instance dictionary, the class, and its parent classes. If it finds nothing, it throws an AttributeError.

But if you define __getattr__ on that class, Python will call that method instead of raising the error. It passes in the name of the attribute you tried to access (as a string), and you get to decide what happens next.

Let's look at a bare-bones example from PythonSkillset's own codebase.

class FlexibleDocument:
    def __getattr__(self, name):
        # Instead of erroring, let's return a friendly message
        return f"The attribute `{name}` does not exist. But maybe you meant 'title'?"

If you create an instance of this and try doc.body, instead of getting an error, you get a string telling you what went wrong. Not super useful yet, but it shows the core idea.

The difference between __getattr__ and __getattribute__

If you've poked around Python internals before, you might have seen __getattribute__. That one is called every single time you access an attribute, even if it already exists. __getattr__ is only called as a last resort.

This distinction is important. You almost always want __getattr__ unless you have a very specific reason to intercept every attribute access (which, trust me, can get messy fast).

A practical example: settings with fallbacks

Imagine you're building a configuration object. You know that many settings exist, but you also want to allow for user-defined defaults if one is missing. Here's how PythonSkillset handled it for a recent project.

class AppConfig:
    defaults = {
        'debug': False,
        'port': 8080,
        'database_url': 'sqlite:///default.db'
    }

    def __init__(self, user_config=None):
        self._user_config = user_config or {}

    def __getattr__(self, name):
        # Check user's config first
        if name in self._user_config:
            return self._user_config[name]
        # Then check defaults
        if name in self.defaults:
            return self.defaults[name]
        # If nothing matches, raise a clear error
        raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")

Now, you can do this:

config = AppConfig({'debug': True})
print(config.debug)        # True - from user config
print(config.port)         # 8080 - from defaults
print(config.database_url) # sqlite:///default.db - from defaults

No need for dictionary lookups like config.get('debug'). It just feels like a natural object.

Where it shines: proxies and wrappers

One of the most common uses for __getattr__ is building a proxy object. Imagine you have a dictionary of data that comes from an API, and you want to access it with dot notation.

class APIResponse:
    def __init__(self, data):
        self._data = data

    def __getattr__(self, name):
        try:
            return self._data[name]
        except KeyError:
            raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")

Now, instead of writing response['user']['email'], you can write response.user.email. It's cleaner, and it reads more like a natural sentence.

A warning about side effects

Here's where a lot of people get tripped up. Because __getattr__ only runs when an attribute isn't found, it behaves a bit differently with things like hasattr(). Python's hasattr works by trying to access the attribute and catching an AttributeError. If your __getattr__ never raises one, hasattr will always return True, even for attributes that don't logically exist.

Also, if you use pickle or other serialization libraries that look for special methods like __getstate__, your __getattr__ can accidentally intercept those calls and cause odd bugs. The solution is to always check for "special" attributes (those starting with double underscores) at the beginning of __getattr__.

def __getattr__(self, name):
    if name.startswith('_'):
        raise AttributeError(name)
    # ... rest of logic

Wrapping up

__getattr__ is one of those Python features that feels niche until you need it. Then suddenly, you see it everywhere. Dynamic configuration, lazy-loaded objects, proxy patterns, mocking in tests—it's a surprisingly versatile tool.

The key takeaway is this: use it when you want Python to be smart about what to return for undefined attributes. Don't use it to mask real bugs or to replace a simple dictionary. When applied properly, it makes your code feel more intuitive and less cluttered.

Try it on your next project. You might find yourself missing it when you go back to plain classes.

Comments

Questions, corrections, and tips stay visible for everyone reading this page.

0 in thread

Join the discussion

Shown next to your comment.

Up to 4,000 characters

No comments yet

Be the first to leave a note — it helps the next reader.