getattr vs getattribute in Python
Python provides two primary magic methods for intercepting attribute
access on objects: __getattr__ and
__getattribute__. While both methods allow developers to
customize how attributes are retrieved, they operate at fundamentally
different stages of the lookup process. This article breaks down the
mechanics of each method, illustrates their execution flow, highlights
the risk of infinite recursion, and clarifies when to use one over the
other.
The Core Difference
The primary distinction between the two methods lies in when they are invoked by the Python interpreter:
__getattribute__is invoked unconditionally every time an attribute is accessed on an object, regardless of whether the attribute actually exists in the object's namespace.__getattr__is invoked conditionally as a fallback mechanism. It is only called when an attribute cannot be found through normal lookup mechanisms (i.e., after looking in the instance dictionary, class attributes, descriptors, and raising anAttributeError).
How __getattribute__
Works
Whenever you access an attribute via dot notation (e.g.,
obj.name), Python implicitly calls
obj.__getattribute__('name'). Because it executes on every
single attribute lookup, it provides complete control over attribute
resolution.
The Recursion Trap
Because __getattribute__ intercepts all
lookups, referencing an attribute inside it using self.attr
or self.__dict__ triggers another call to
__getattribute__, resulting in an infinite recursion error
(RecursionError).
To safely access attributes inside __getattribute__, you
must route the lookup through the base class implementation using
super():
class Interceptor:
def __init__(self, value):
self.value = value
def __getattribute__(self, name):
print(f"Intercepting access to: {name}")
# Safe access using super()
return super().__getattribute__(name)
obj = Interceptor(42)
print(obj.value)
# Output:
# Intercepting access to: value
# 42How __getattr__ Works
__getattr__ is designed for handling missing attributes.
If Python resolves an attribute successfully through standard lookup (or
via __getattribute__), __getattr__ is never
invoked.
This makes __getattr__ much safer and more performant
for typical use cases, such as dynamic delegation or providing default
values:
class DynamicFallback:
def __init__(self):
self.existing = "I exist"
def __getattr__(self, name):
print(f"Attribute '{name}' not found. Handling dynamically.")
return f"default_{name}"
obj = DynamicFallback()
# Normal lookup: __getattr__ is NOT called
print(obj.existing) # Output: I exist
# Missing attribute: __getattr__ IS called
print(obj.missing)
# Output:
# Attribute 'missing' not found. Handling dynamically.
# default_missingThe Attribute Lookup Order
When an attribute obj.x is accessed, Python follows this
sequence:
obj.__getattribute__('x')is called.- The default implementation checks descriptors, the instance
__dict__, and the class hierarchy. - If the attribute is found, it is returned.
- If the attribute is not found, Python raises an
AttributeError. - If defined,
obj.__getattr__('x')catches theAttributeErrorand handles the request. - If
__getattr__is not implemented, theAttributeErrorbubbles up to the caller.
Practical Comparison
| Feature | __getattr__ |
__getattribute__ |
|---|---|---|
| Invocation | Only when attribute is not found | On every attribute access |
| Performance Impact | Negligible on existing attributes | Overhead on every lookup |
| Recursion Risk | Low | High |
| Base Class Call | Generally not required | Required via super() |
| Common Use Case | Proxies, adapters, dynamic attributes | Profiling, security wrappers, auditing |
When to Use Which
Use __getattr__ in the vast majority of
scenarios. It is the appropriate choice when implementing proxies,
delegating method calls to wrapped objects, or handling lazy-loaded
attributes. It leaves Python's fast, default lookup mechanisms untouched
for attributes that exist.
Use __getattribute__ only when absolute
control over the object's namespace is required. Common scenarios
include building debugging tools, creating security proxies that hide
existing attributes, or deeply integrating with custom data-binding
frameworks.