Python __getattribute__: How to Intercept Attribute Access
python **getattribute**: Learn how Python's __getattribute__ method controls attribute access, how to override it safely, and when to use it instead of __getattr__.
python getattribute requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
In Python, __getattribute__ is invoked whenever you access an attribute on an object. This includes direct attribute access like obj.attr, as well as access through getattr(). By default, the method performs the standard attribute lookup: it checks the instance's __dict__, then the class and its bases, and finally invokes descriptors if present. Overriding __getattribute__ gives you a hook to intercept every attribute read, which is useful for validation, logging, lazy loading, or implementing dynamic behavior.
What __getattribute__ Does
Every object in Python has a __getattribute__ method inherited from object. When you write obj.attr, Python calls type(obj).__getattribute__(obj, 'attr'). The method must return the attribute value or raise AttributeError. If it raises AttributeError, Python then calls __getattr__ if it is defined, allowing a fallback mechanism.
Here is a minimal override:
class MyClass: def __getattribute__(self, name): print(f"Accessing {name}") return super().__getattribute__(name)
In this example, every attribute access prints a message before delegating to the default implementation via super().__getattribute__(name). Note that super() returns a proxy that calls the method on the parent class, avoiding recursion.
How Attribute Lookup Works by Default
The default object.__getattribute__ follows a specific order:
- It looks for a data descriptor (a class attribute that implements both
__get__and__set__). - It checks the instance's
__dict__. - It looks for a non-data descriptor (implements
__get__only). - It falls back to the class attribute.
This order is why descriptors like property work. When you override __getattribute__, you replace this entire mechanism, so you must be careful to preserve the behavior you need.
Overriding __getattribute__ Safely
The most common mistake is causing infinite recursion. If you access self.__dict__ inside __getattribute__, that access itself triggers __getattribute__ again. For example:
class Broken: def __getattribute__(self, name): return self.__dict__[name] # RecursionError
The fix is to use object.__getattribute__(self, name) or super().__getattribute__(name). The latter is cleaner when the class inherits from object.
class Safe: def __getattribute__(self, name): if name.startswith('_'): raise AttributeError("Private access") return super().__getattribute__(name)
This override blocks access to any attribute starting with an underscore. It uses super() to delegate to the default implementation, which is safe.
__getattribute__ vs __getattr__
__getattr__ is only called when the normal attribute lookup fails (i.e., when __getattribute__ raises AttributeError). It is often used for dynamic attributes. The table below highlights the differences:
| Behavior | __getattribute__ | __getattr__ |
|---|---|---|
| Called for every attribute access | Yes | Only after AttributeError |
| Default implementation | Performs standard lookup | None (raises AttributeError) |
| Typical use | Intercepting all reads | Providing fallback attributes |
| Recursion risk | High if not careful | Lower, but still possible |
Choosing between them depends on need. If you need to intercept every attribute read, use __getattribute__. If you only need to handle missing attributes, __getattr__ is simpler and less risky.
Performance and Recursion Pitfalls
Overriding __getattribute__ adds overhead to every attribute access, not just the ones you care about. In performance-sensitive code, this can slow down loops and frequent lookups. For example, a logging wrapper that prints every access can degrade performance significantly in a hot path.
Recursion is another pitfall. Any attribute access inside __getattribute__ will call itself unless you use object.__getattribute__ or super().__getattribute__. This includes accessing self.__class__, self.__dict__, or even calling a method on self. A common pattern is to use super().__getattribute__(name) to delegate, which avoids recursion because super() returns a proxy that bypasses the overridden method.
When to Use __getattribute__ in Real Code
__getattribute__ is rarely needed in everyday Python. It is more common in frameworks, ORMs, and metaprogramming libraries. For instance, you might use it to:
- Enforce access control based on attribute names.
- Implement lazy loading of expensive attributes.
- Create a proxy that forwards attribute access to another object.
- Add logging or monitoring to an object's attribute reads.
Consider a proxy example:
class Proxy: def __init__(self, target): self._target = target def __getattribute__(self, name): if name == '_target': return super().__getattribute__(name) target = super().__getattribute__('_target') return getattr(target, name)
Here, accessing proxy.some_method forwards to the underlying target object. The special case for _target prevents infinite recursion when accessing the target itself.
Common Mistakes and Edge Cases
One common mistake is assuming __getattribute__ is called for special methods like __len__ or __iter__. In CPython, implicit special method lookups bypass __getattribute__ in some cases. For example, len(obj) may call type(obj).__len__ directly, not obj.__len__. This means overriding __getattribute__ does not intercept implicit calls to magic methods. If you need to control those, you must override the specific magic method instead.
Another edge case is handling AttributeError correctly. If your override raises AttributeError for a name that does not exist, Python will then call __getattr__ if it is defined. This is the intended fallback chain. But if you accidentally raise AttributeError for a name that does exist, you break normal behavior.
Finally, be aware that __getattribute__ is called for private name mangling. Names like _ClassName__attr are accessed normally, and your override will see the mangled name, not the original source name. This can be surprising when you try to filter attributes by prefix.
Understanding __getattribute__ gives you low-level control over attribute access, but it comes with complexity and performance costs. Use it only when the standard attribute machinery is insufficient, and always delegate to super().__getattribute__ to avoid recursion.