skip to content

How does `__getattribute__` differ from `__getattr__` on a Python class?

level: middleimportance: must knowfreq 55%

answer

  1. One runs always, one runs on failure
  2. Every access versus the miss path
  3. Overriding it replaces the whole lookup
  4. Existing names are invisible to the fallback
  5. Delegate with object.__getattribute__ or super()

basics

~20 s

getattribute is called for every attribute access on an instance and performs the whole lookup itself; getattr is only the fallback the runtime calls when that lookup fails with AttributeError. Overriding getattribute touches every access and every internal read.

solid answer

~40 s

`object.__getattribute__` is the implementation of the dot operator: it checks the type for a data descriptor, then the instance `__dict__`, then the class and its bases, and raises `AttributeError` if nothing matches. `__getattr__` is not part of that search at all — it is the second chance the runtime takes when the search raised. So overriding `__getattribute__` means replacing the whole algorithm and intercepting **every** read, including `self.something` inside your own methods, reads by debuggers and reads by pickling code; each one becomes a Python-level call rather than the interpreter's fast C path. Overriding `__getattr__` costs nothing on the success path. The rule of thumb: reach for a `property` or a descriptor first, `__getattr__` next, and `__getattribute__` only when you must intercept names that genuinely exist.

code

python · 16 lines
python
class Audited:
    def __init__(self):
        self.sku = "A-1"

    def __getattribute__(self, name):
        print("every access:", name)
        return object.__getattribute__(self, name)

    def __getattr__(self, name):
        print("miss only:", name)
        return None


a = Audited()
a.sku       # every access: sku
a.missing   # every access: missing / miss only: missing

go deeper

for a junior

Remember which one is which: getattribute handles every access, getattr only the misses. Knowing that overriding the first one is rare and risky is enough at this level.

for a middle

Explain the mechanics of the default implementation, why the fallback cannot see existing names, and why a custom getattribute must delegate to object.getattribute or super().getattribute to return anything at all.

for a senior

Show the decision ladder — property or descriptor, then getattr, then getattribute last — and be able to name the cases that genuinely require total interception, such as denying an existing attribute or auditing successful reads.

for a principal

Own the cost of dynamic attribute surfaces across a codebase: type checkers and editors go blind, reviews get harder, and hot objects lose the interpreter's fast path. Decide where dynamism is worth it and where a generated explicit class wins.

## Two hooks, two positions in the pipeline `obj.name` calls `type(obj).__getattribute__(obj, "name")`. The default implementation, `object.__getattribute__`, is a C function that runs the familiar search: a data descriptor found on the type wins, otherwise the instance `__dict__`, otherwise a class attribute or non-data descriptor from the MRO; failing all of that it raises `AttributeError`. `__getattr__` never participates in that search. It is a separate hook the runtime consults *after* `__getattribute__` has raised `AttributeError`. That single structural difference produces every practical consequence: | | `__getattribute__` | `__getattr__` | |---|---|---| | when it runs | every access, first | only after lookup failed | | sees existing attributes | yes | no | | cost when the name exists | a Python call per access | nothing | | typical use | total interception, auditing, virtual objects, denial | lazy attributes, proxies, record wrappers | | risk | recursion, breadth of blast radius | silently swallowing real errors | ## What overriding `__getattribute__` really commits you to You are replacing the lookup algorithm, so your method must produce a value for *everything* the object exposes — methods, `__class__`, `__dict__`, the lot. In practice that means ending with a delegation to `object.__getattribute__(self, name)` or, inside a class hierarchy, `super().__getattribute__(name)`, which keeps cooperative bases in the MRO in play instead of jumping straight to `object`. The blast radius is wider than people expect. Every `self.x` read in your own methods now runs your hook. So does the read a debugger performs while rendering the object, the read `repr` does, and the probes copying and pickling code make. If your hook logs, every one of those becomes a log line; if it validates, every one pays the validation. There is a performance dimension too. Attribute access on ordinary objects is one of the most heavily optimised operations in CPython, resolved in C with inline caching. Defining `__getattribute__` puts a Python-level function in that path for the whole object, so hot loops that touch attributes can slow noticeably. This is not a reason never to use it — it is a reason not to put it on the objects in your inner loop. ## Recursion is the immediate hazard Because the hook runs on every access, reading `self.anything` inside it re-enters it. The fix is to route internal reads through `object.__getattribute__(self, name)`. That trap is common enough to be its own interview question; the important half here is *why* it exists, which is the "every access" property. ## When `__getattribute__` is the right answer Reach for it only when you must intercept names that already exist, because `__getattr__` structurally cannot: * **Access control / denial** — a wrapper that hides some of the wrapped object's real attributes, or refuses them unless a capability is present. * **Auditing and tracing** — recording every read for a debugging session or a test double, where a read that succeeds is exactly what you want to see. * **Virtual objects and remote views** — where the object has no real state of its own and the whole namespace is computed or fetched. * **Read-only or frozen views** — often paired with `__setattr__` to reject writes. For anything narrower there is a cheaper tool. A computed value on a known name is a `property`. A reusable validation or storage policy across several names is a descriptor on the class. A name that does not exist yet is `__getattr__`. Reaching for `__getattribute__` as a first instinct is a strong signal in a review that the design has not been decomposed. ## Combining the two Nothing stops a class from defining both, and the interaction follows straight from the ordering: your `__getattribute__` runs first; if it raises `AttributeError`, the runtime then calls `__getattr__`. A proxy sometimes uses that deliberately — `__getattribute__` handles the handful of names the proxy itself owns and raises for everything else, and `__getattr__` forwards the rest to the wrapped object. One more placement note: defining `__getattribute__` on a *metaclass* intercepts attribute access on the class object rather than on its instances. That is a different object being accessed, and it is a frequent source of confusion when a hook "does not fire" for `MyClass.attr`. ## How to talk about the pair in an interview State the pipeline in one sentence — the dot operator calls `__getattribute__`, and only an `AttributeError` escaping it reaches `__getattr__` — and then derive everything else from it rather than reciting a list. The fallback cannot see existing names *because* it runs after a successful lookup would have returned. The fallback is free *because* it is on the failure path. The every-access hook must delegate *because* it replaced the algorithm. That derivation is what separates a candidate who has used these hooks from one who has memorised two dunder names.

  • Why can a proxy built on `__getattr__` not hide an attribute the proxy class already defines?
    Because the fallback is never consulted for a name the ordinary lookup finds. If the proxy class defines `close`, then `proxy.close` resolves to the proxy's own method and forwarding never happens. Hiding or overriding an existing name requires `__getattribute__`, which sees every access — or requires the proxy to keep its own namespace deliberately tiny so collisions cannot occur.
  • Inside `__getattribute__`, when would you prefer `super().__getattribute__(name)` over `object.__getattribute__(self, name)`?
    Whenever the class sits in a hierarchy where a base may also customise attribute access. `super()` follows the MRO from your class, so an intermediate base's implementation still runs; `object.__getattribute__` jumps straight to the default and silently skips those bases. Use `object.` only when you deliberately want the raw default, typically for reading one internal attribute of your own.
  • What is the performance cost of defining `__getattribute__` on a hot class?
    Every attribute read on that object goes from an optimised C path to a Python function call, plus whatever work the hook does, plus the delegating call back into `object.__getattribute__`. On an object touched in a tight loop that can dominate. Keep the hook off inner-loop objects; a `property` on the one name that needs behaviour leaves every other access on the fast path.

__getattribute__ is a doorman who stops and inspects every single person entering the building; __getattr__ is the lost-and-found desk you are sent to only when what you wanted was not inside.

saying these in an interview costs you the question

  • Uses the two names interchangeably in the answer
  • Thinks __getattr__ can intercept an existing attribute
  • Overrides __getattribute__ for a single computed name
  • Forgets to delegate, so most attributes vanish
  • Claims __getattribute__ has no performance cost
  • Believes __getattr__ runs before __getattribute__

context