How do I override __getattr__ without breaking the default behavior?
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
In Python, __getattr__ is only called after normal attribute lookup has already failed. That means the easiest way to "not break the default behavior" is to remember that normal behavior already runs first, and your __getattr__ should only provide a fallback for truly missing attributes.
The two rules that matter most are: do not create infinite recursion by touching missing attributes carelessly inside __getattr__, and raise AttributeError when you cannot provide the requested attribute. If you violate either rule, introspection and ordinary attribute access start behaving strangely.
What __getattr__ Actually Does
Python attribute lookup normally checks the instance, the class, and the inheritance chain. Only if that fails does Python call __getattr__.
So this works as a pure fallback hook:
cfg.name is found normally. cfg.timeout is not found, so __getattr__ provides a fallback value.
Always Raise AttributeError for Unknown Names
The correct failure mode for __getattr__ is AttributeError:
This matters because Python tools rely on AttributeError to understand whether an attribute genuinely exists. If you return None for everything instead, functions like hasattr, IDE completion, and some libraries can behave incorrectly.
Avoid Infinite Recursion
A classic mistake is accidentally triggering __getattr__ from inside itself:
If missing_value does not exist, Python calls __getattr__ again, which asks for missing_value again, and so on until recursion fails.
When you need direct attribute access inside your implementation, use object.__getattribute__:
That bypasses __getattr__ and avoids the recursive trap.
__getattr__ Versus __getattribute__
This distinction is important:
- '
__getattr__runs only for missing attributes' - '
__getattribute__runs for every attribute access'
If your goal is just to add fallback behavior, prefer __getattr__. Overriding __getattribute__ is much more invasive and much easier to get wrong.
A lot of "breaking default behavior" problems come from using __getattribute__ when __getattr__ would have been enough.
A Realistic Delegation Example
A common use case is proxying unknown attributes to another object:
Here, ordinary attributes on Wrapper still work normally, and only missing ones are delegated to the wrapped object.
When You Need Parent Fallback Behavior
If a base class also defines __getattr__, you can delegate to it explicitly:
That is the right way to extend an inherited fallback instead of silently replacing it.
Common Pitfalls
The biggest pitfall is returning a default value for every unknown attribute. That may feel convenient, but it breaks normal Python expectations and makes debugging much harder.
Another common issue is recursion caused by reading missing attributes through self.some_name inside __getattr__. Use object.__getattribute__ when you need internal state safely.
Developers also forget that __getattr__ is not called for attributes that already exist. If you need to intercept all lookups, that is a __getattribute__ problem, not a __getattr__ one.
Finally, always raise AttributeError for unsupported names. That keeps hasattr, getattr(..., default), and Python's introspection behavior working correctly.
Summary
- '
__getattr__is a fallback hook for missing attributes, not a replacement for normal lookup.' - To preserve default behavior, let normal lookup work first and only handle truly missing names.
- Raise
AttributeErrorwhen you cannot provide the requested attribute. - Use
object.__getattribute__inside__getattr__when you need internal state without recursion. - Prefer
__getattr__over__getattribute__unless you really need to intercept every attribute access.
Related reading
- How do I parallelize a simple Python loop?
- How do I pass a method as a parameter in Python
- How do I pass a string into subprocess.Popen using the stdin argument?
- How do I pass an async function to a thread target in Python?
- How do I plot in real-time in a while loop?
- How do I prepend to a short python list?
- How do I prevent Conda from activating the base environment by default?
- How do I print an exception in Python?
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.