Back to Blog
Python

Python getattr vs getattribute: Key Differences

python **getattr** vs **getattribute**: Understand the difference between Python's getattr and getattribute, when to override each, and how to avoid recursion and perf...

PythonAttribute AccessgetattrgetattributeDunder MethodsObject Introspection
Diagram comparing Python's getattr and getattribute attribute lookup methods

When you access an attribute in Python, the interpreter calls either __getattr__ or __getattribute__ depending on the situation. Understanding the difference between python getattr vs getattribute is essential for writing custom attribute behavior without breaking normal object operations.

How Python Resolves Attribute Access

Every attribute access on an object goes through __getattribute__ first. This method is called unconditionally for every attribute lookup, whether the attribute exists or not. Its default implementation performs the actual lookup in the object's __dict__, then in the class and its base classes, and finally calls __getattr__ if the attribute was not found.

The __getattr__ method is only invoked when the normal attribute lookup fails. It is not called if the attribute is found through the standard mechanism. This distinction is the core of the getattr vs getattribute comparison.

class Example: def __getattribute__(self, name): print(f"__getattribute__ called for {name}") return super().__getattribute__(name) def __getattr__(self, name): print(f"__getattr__ called for {name}") raise AttributeError(name) e = Example() e.existing = 1 print(e.existing) # __getattribute__ called for existing print(e.missing) # __getattribute__ called for missing, then __getattr__ called

In the example, __getattribute__ runs for both existing and missing. __getattr__ only runs for missing because the normal lookup failed.

Overriding __getattribute__: When and How

Overriding __getattribute__ gives you control over every attribute access. This is useful for logging, access control, or transforming attribute values before they are returned. However, you must be careful to call super().__getattribute__ to avoid infinite recursion.

class ReadOnlyProxy: def __init__(self, obj): self._obj = obj def __getattribute__(self, name): if name.startswith('_'): return super().__getattribute__(name) return getattr(self._obj, name)

Here, internal attributes like _obj are accessed normally, while public attributes are delegated to the wrapped object. Without the super() call, any attribute access would recursively call __getattribute__ again, causing a RecursionError.

Overriding __getattr__: When and How

__getattr__ is the right place to handle missing attributes gracefully. Common uses include returning a default value, generating dynamic attributes, or delegating to a fallback object. Because it is only called when the attribute is absent, it does not interfere with normal attribute access.

class DynamicConfig: def __init__(self, defaults): self._defaults = defaults def __getattr__(self, name): if name in self._defaults: return self._defaults[name] raise AttributeError(name) config = DynamicConfig({'timeout': 30}) print(config.timeout) # 30 print(config.retries) # AttributeError

This pattern is safe because __getattr__ only runs when the attribute is not found in the normal way. If the attribute exists, the default lookup wins.

Common Pitfalls and Recursion Errors

The most frequent mistake is overriding __getattribute__ without calling super(). Even accessing self.__dict__ inside __getattribute__ triggers another __getattribute__ call, leading to infinite recursion.

class Broken: def __getattribute__(self, name): return self.__dict__[name] # RecursionError

The correct way is to use super().__getattribute__(name) or object.__getattribute__(self, name) to bypass the override.

Another pitfall is using hasattr() in __getattr__. Since hasattr() calls getattr() internally, this can cause recursive behavior. Instead, check the instance's __dict__ directly or use try/except.

Performance and Maintainability Considerations

Overriding __getattribute__ adds overhead to every attribute access, even for attributes that exist. This can slow down hot code paths. __getattr__ only runs when an attribute is missing, so its performance impact is limited to failure cases.

If you need to intercept attribute access for a large number of objects, consider whether a simpler approach like a property or a custom descriptor would meet your needs. Descriptors are more explicit and often easier to maintain than a blanket __getattribute__ override.

When you do override __getattribute__, keep the logic minimal and delegate to super() as early as possible. This reduces the chance of introducing bugs and keeps the runtime cost predictable.

Choosing Between __getattr__ and __getattribute__

Use __getattr__ when you only need to handle missing attributes. It is the safer, more targeted choice and does not affect existing attribute lookups.

Use __getattribute__ when you must intercept every attribute access, including existing ones. This is necessary for proxies, logging, or dynamic behavior that depends on the attribute name.

class Proxy: def __init__(self, target): self._target = target def __getattribute__(self, name): if name.startswith('_'): return super().__getattribute__(name) return getattr(self._target, name)

In this proxy, __getattribute__ is the right tool because it must forward all public attribute accesses. If you only needed a default value for missing attributes, __getattr__ would be simpler and less invasive.

Edge Cases and Compatibility

Special methods like __len__ or __iter__ are looked up on the type, not on the instance. This means that overriding __getattribute__ does not affect how Python resolves these special methods. If you need to intercept those, you must define them explicitly on the class.

Also, __getattr__ is not called for attributes that are found in the class's method resolution order. If a class defines a method, __getattr__ will not be invoked for that name, even if you want to override it dynamically. In such cases, you would need to modify the class or use a metaclass.

Understanding these boundaries helps you avoid surprising behavior when combining attribute interception with inheritance or special method protocols.

python **getattr** vs **getattribute**: Practical Usage and | RYUSLOG DEV