Back to Blog
Python

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__.

PythonMagic MethodsAttribute AccessMetaprogrammingObject-Oriented Programming
A diagram showing Python attribute access interception with __getattribute__.

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:

  1. It looks for a data descriptor (a class attribute that implements both __get__ and __set__).
  2. It checks the instance's __dict__.
  3. It looks for a non-data descriptor (implements __get__ only).
  4. 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 accessYesOnly after AttributeError
Default implementationPerforms standard lookupNone (raises AttributeError)
Typical useIntercepting all readsProviding fallback attributes
Recursion riskHigh if not carefulLower, 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.

python **getattribute**: Practical Usage and Code Examples | RYUSLOG DEV