__getattr__ and __getattribute__: Custom Attribute Access
The magic methods __getattr__ and __getattribute__ allow you to intercept and customize how attributes are accessed on an object. __getattribute__ is invoked for every attribute access and is called first; __getattr__ is called only when an attribute is not found through normal lookup. Confusing the two is a common pitfall that leads to infinite recursion and subtle bugs. Understanding when to use each—and how they interact with Python's attribute-resolution chain—is essential for building proxy objects, lazy-loading systems, and frameworks that wrap third-party APIs.
__getattribute__ vs. __getattr__: When Each Is Called
__getattribute__ is the lower-level mechanism: Python calls it for every attribute access on an instance, even if the attribute exists. __getattr__ is called only as a fallback when an attribute is not found through the normal chain. This distinction is critical: implementing __getattribute__ on a high-frequency accessor is expensive; implementing __getattr__ is cheap because it's only invoked on cache misses.
Here's the lookup order: (1) instance __dict__, (2) descriptor in class, (3) class __dict__, (4) parent classes, (5) if all fail, call __getattr__. If you override __getattribute__, you short-circuit this entire chain unless you explicitly call super().__getattribute__().
Using __getattr__ for Lazy Loading
__getattr__ is most commonly used to provide fallback behavior or lazy-load attributes on first access. Here's a practical example: a wrapper around an HTTP API client that loads resource data only when accessed:
import requests
class APIResource:
"""Lazy-loading wrapper around a remote API resource."""
def __init__(self, endpoint, resource_id):
self.endpoint = endpoint
self.resource_id = resource_id
self._data = None # Will be loaded on first access
def __getattr__(self, name):
# Called only when 'name' is not found in instance or class dict
if name.startswith('_'):
raise AttributeError(f"'{type(self).__name__}' has no attribute '{name}'")
# Lazy-load data from API on first access
if self._data is None:
response = requests.get(f"{self.endpoint}/{self.resource_id}")
self._data = response.json()
# Return the attribute from the loaded data
if name in self._data:
return self._data[name]
raise AttributeError(f"Resource has no field '{name}'")
# Usage
user = APIResource("https://api.example.com/users", 42)
print(user.name) # Makes HTTP request, returns name from JSON
print(user.email) # Uses cached data, no second request
This pattern is used in database query libraries (SQLAlchemy lazy-loads relationships) and API clients (boto3, google-cloud-python) to defer expensive operations until needed.
Using __getattribute__ for Proxies and Introspection
__getattribute__ is powerful but dangerous: every attribute access goes through it, including access to methods and special attributes. Misuse causes infinite recursion. Here's a safe proxy pattern:
class TracingProxy:
"""Logs every attribute access on a wrapped object."""
def __init__(self, target):
# Use object.__setattr__ to bypass __setattr__ interception
object.__setattr__(self, '_target', target)
object.__setattr__(self, '_access_log', [])
def __getattribute__(self, name):
# Access _target and _access_log using object's __getattribute__ to avoid recursion
target = object.__getattribute__(self, '_target')
log = object.__getattribute__(self, '_access_log')
# Avoid recursing on special attributes
if name in ('_target', '_access_log'):
return object.__getattribute__(self, name)
# Log the access
log.append(name)
# Delegate to the real object
return getattr(target, name)
def access_history(self):
return object.__getattribute__(self, '_access_log')
# Usage
obj = TracingProxy({'key': 'value'})
result = obj.get('key')
print(obj.access_history()) # Output: ['get']
The key to avoiding recursion is using object.__getattribute__() to access your own internal state (_target, _access_log), bypassing your custom logic. This is a defensive pattern essential when implementing proxies or aspect-oriented code.
Delegation Pattern: Wrapping Objects Cleanly
A common metaprogramming task is wrapping an object to add behavior without modifying its type. The delegation pattern uses __getattr__ to forward unknown attributes:
from functools import wraps
class CachedWrapper:
"""Wraps an object and caches method results."""
def __init__(self, target, cache_methods=None):
self._target = target
self._cache = {}
self._cache_methods = cache_methods or []
def __getattr__(self, name):
# Forward to the wrapped object
attr = getattr(self._target, name)
# If it's a cached method, wrap it with caching logic
if name in self._cache_methods and callable(attr):
@wraps(attr)
def cached_call(*args, **kwargs):
# Create a cache key from method name and arguments
key = (name, args, tuple(sorted(kwargs.items())))
if key not in self._cache:
self._cache[key] = attr(*args, **kwargs)
return self._cache[key]
return cached_call
return attr
class DataFetcher:
def get_user(self, user_id):
print(f"Fetching user {user_id}...")
return {"id": user_id, "name": "Alice"}
# Usage
fetcher = DataFetcher()
cached_fetcher = CachedWrapper(fetcher, cache_methods=['get_user'])
print(cached_fetcher.get_user(1)) # Fetches: "Fetching user 1..."
print(cached_fetcher.get_user(1)) # Returns cached result, no print
This pattern enables adding cross-cutting concerns (caching, logging, rate-limiting) to existing objects without modifying their source code—a powerful technique in frameworks.
Pitfalls: Infinite Recursion and Performance
The most common bug with __getattribute__ is infinite recursion. If your custom __getattribute__ accesses self.x, and self.x doesn't exist, Python calls your __getattribute__ again, which calls self.x, and so on. Always use object.__getattribute__() to access your own state or explicitly call super().__getattribute__().
Second, implementing __getattribute__ slows down every attribute access. A tight loop calling an attribute 1 million times will be noticeably slower if you override __getattribute__. Prefer __getattr__ when possible, or use __slots__ to reduce attribute-lookup overhead.
Comparison: __getattr__ vs. __getattribute__ vs. Properties
| Mechanism | Called When | Use Case | Cost |
|---|---|---|---|
__getattr__ | Attribute not found (fallback) | Lazy loading, dynamic attributes, API proxies | Low (fallback only) |
__getattribute__ | Every attribute access | Proxies, logging, introspection | High (on every access) |
@property descriptor | Specific named attribute | Computed properties, validation | Low (specific attribute) |
Key Takeaways
__getattr__is called only when an attribute is not found; use it for fallback behavior and lazy loading.__getattribute__is called for every attribute access; use it sparingly and always guard against infinite recursion withobject.__getattribute__().- Delegation patterns with
__getattr__cleanly add behavior to wrapped objects without modifying their type. - Proxies and API wrappers are idiomatic uses of
__getattr__; most developers never need to override__getattribute__.
Frequently Asked Questions
Why does my __getattribute__ implementation cause infinite recursion?
If you access self.attr inside __getattribute__, Python calls your custom __getattribute__ again, creating a loop. Always use object.__getattribute__(self, 'attr') to access your own state, bypassing your custom logic.
Should I use __getattr__ or @property for computed attributes?
Use @property if the attribute is known at class definition time and belongs to a single instance. Use __getattr__ if the attribute is dynamic, unknown at class time, or shared across many attributes (e.g., forwarding to a wrapped object).
How does __getattr__ interact with __slots__?
If a class uses __slots__ and you define __getattr__, it will still be called for any attribute not in __slots__. However, instance __dict__ is not available with __slots__, so your __getattr__ cannot use self.__dict__.
Can I use __getattr__ on a __dict__ entry for introspection?
Yes. If you access self.__dict__ inside __getattr__, Python calls it recursively because looking up __dict__ triggers __getattr__. Use object.__getattribute__(self, '__dict__') to safely access the instance dictionary.
Further Reading
- Python Data Model: Attribute Access — official docs on
__getattribute__and__getattr__. - Descriptor Protocol and Magic Methods — comprehensive guide to Python magic methods and attribute access.
- Proxy Pattern in Python — detailed proxy pattern examples and best practices.
- Avoiding Infinite Recursion in getattribute — StackOverflow discussion of common pitfalls.