skip to content

Why can Python's built-in hasattr() report False for an attribute that is genuinely defined?

level: middleimportance: nice to knowfreq 18%

answer

  1. It calls getattr under the hood
  2. Only one exception type is swallowed
  3. The getter runs, side effects and all
  4. A raise inside the property reads as absent
  5. getattr with a default asks it once

basics

~20 s

hasattr() is defined as calling getattr() and returning False if AttributeError is raised. So if the attribute is a property or a dynamic lookup whose own code raises AttributeError, the guard reports the attribute as absent and hides the real error inside it.

solid answer

~50 s

`hasattr(obj, name)` is not an inspection of a class dictionary; it performs the actual attribute access and reports `False` when that access raises `AttributeError`, `True` otherwise. That has two consequences. It **runs the getter** — a `property`, a `__getattr__`, or a descriptor executes with all its cost and side effects just to answer a boolean. And it **cannot distinguish "not defined" from "the getter itself raised AttributeError"**: a `property` whose body touches a missing internal attribute raises `AttributeError` from inside, and `hasattr()` reports `False`, so a real bug is silently reported as a missing feature. Since Python 3.2 only `AttributeError` is swallowed — every other exception propagates — but that is exactly the type such bugs raise. Prefer `getattr(obj, name, sentinel)` when you want a default, or simply attempt the operation and catch `AttributeError` around the use itself.

code

python · 12 lines
python
class Invoice:
    @property
    def total(self):
        return self._rows.total


inv = Invoice()
print(hasattr(inv, 'total'))
try:
    inv.total
except AttributeError as exc:
    print('real cause:', exc)

go deeper

for a junior

Know that hasattr(obj, name) actually performs the attribute access and answers False when it raises AttributeError, and that getattr with a third argument is usually the better tool when you want a value with a fallback.

for a middle

Explain the mechanism: hasattr is specified in terms of getattr, so descriptors and property getters run, and an AttributeError raised inside the getter is indistinguishable from the attribute not existing at all.

for a senior

Be able to describe the failure this causes in production — a feature that silently disappears with no exception and no traceback — and the review rule that follows: never let a boolean guard stand in for the access whose exception you care about.

for a principal

Own how the codebase expresses capability: declared protocols and explicit interfaces checked statically, rather than scattered runtime attribute probes whose answers depend on getter side effects and cannot be reasoned about across teams.

### What hasattr actually does It is tempting to read `hasattr(obj, 'total')` as "is `total` present in the class or instance dictionary?". It is not. The builtin is specified in terms of `getattr()`: it calls `getattr(obj, name)`, returns `True` if that returns, and returns `False` if it raises `AttributeError`. Every mechanism that participates in attribute lookup therefore runs: descriptors, `property` getters, `__getattr__`, `__getattribute__`, and any `__slots__` or metaclass machinery involved. Two consequences follow, and both are the reason this LBYL guard is unreliable even in a single-threaded program with no races anywhere. ### Consequence 1: the check has the cost and the side effects of the operation If `total` is a `property` that queries a database, walks a large structure, or lazily builds a cached value, then `hasattr(invoice, 'total')` does that work — and if you then read `invoice.total`, you have done it twice. If the getter has side effects, the "check" performs them. A guard is supposed to be cheaper and safer than the operation; here it *is* the operation, plus a discarded result. ### Consequence 2: it confuses "absent" with "the getter is broken" This is the interview point. Consider: ```python class Invoice: @property def total(self): return self._rows.total # _rows was never set ``` Reading `invoice.total` raises `AttributeError: 'Invoice' object has no attribute '_rows'` — a real bug, with a real traceback pointing at the line. But `hasattr(invoice, 'total')` catches that `AttributeError` and answers `False`. Calling code written as `if hasattr(invoice, 'total'): use_it()` silently takes the "no total" branch. The feature quietly stops working, no exception ever surfaces, and the traceback that named `_rows` is gone. The same trap appears in any `__getattr__` implementation that computes something and can raise `AttributeError` from within, and in property chains where a nested access fails. Note the precise scope of the swallowing: **since Python 3.2, `hasattr()` swallows only `AttributeError`.** A `ValueError` or `TypeError` from the getter propagates out of `hasattr()`, which surprises people who expect a boolean function never to raise. On Python 2 `hasattr()` caught *every* exception, which was worse; the 3.x behaviour is a narrowing, not a fix, because `AttributeError` is exactly the type these bugs raise. ### What to use instead **Attempt the operation and catch AttributeError around the use.** This is the EAFP form, and it is the one that keeps the distinction, because the exception object and traceback survive: ```python try: total = invoice.total except AttributeError: total = None ``` This has the same masking property in the narrow sense — the broken getter still raises `AttributeError` — but it fails at the point of use with the real traceback available in the handler, and it makes the swallowing explicit and greppable rather than hidden behind a boolean. **Use getattr with a default when you want a value, not a boolean.** `getattr(obj, name, default)` is one lookup instead of two, and expresses the intent directly: ```python total = getattr(invoice, 'total', None) ``` A unique sentinel (`_MISSING = object()`) distinguishes "absent" from "present and equal to `None`". **When you are checking a shape, not a value, do it structurally.** Repeated `hasattr()` calls to establish that an object "looks like a renderer" are a hand-rolled interface check that runs getters and answers at one instant in time. `typing.Protocol` expresses the required shape for a static checker, and `typing.runtime_checkable` allows an `isinstance()` test against it for the method-presence case. Better still, in most Python code, is to call the method and let the failure be a `TypeError` or `AttributeError` with a traceback that names the offending object. **When you really do mean "is this name defined on the class"**, ask that question instead of the attribute-access question — inspecting the class hierarchy's dictionaries answers about definition without executing any getter. ### Why this belongs in the EAFP discussion The filesystem case shows a look-before-you-leap check going stale because the world changed between the check and the use. `hasattr()` shows something sharper: the check can be **wrong at the moment it runs**, because it is not asking the question you think it is asking. Both push the same conclusion — a guard is an approximation of the operation, while attempting the operation is the operation. The guard may inform a decision; it should never be the reason you believe the following line is safe.

  • What should you use instead when you only need a value with a fallback?
    getattr(obj, name, default). It performs a single lookup instead of the two a hasattr-then-access pattern does, and it states the intent directly. When None is a legitimate value, use a unique module-level sentinel object as the default so you can tell 'absent' from 'present and None'. It still returns the default if the getter raises AttributeError, so it does not fix the masking, but it stops the double evaluation.
  • Does hasattr() run a property's getter?
    Yes. It calls getattr(), so every part of the attribute-lookup machinery executes: descriptors, property getters, __getattr__ and __getattribute__. On a lazily computed or expensive attribute the check does the whole computation and throws the result away, and any side effect in the getter happens. That is another reason the guard is not the cheap, safe pre-check it appears to be.
  • Is hasattr() a reasonable way to check that an object supports a protocol?
    For one well-known method it is common and usually harmless. For a real interface it is a hand-rolled structural check that runs getters and reports on one instant. Prefer typing.Protocol to declare the shape for a static checker, with typing.runtime_checkable plus isinstance when you need a runtime test for method presence. In ordinary Python, calling the method and letting AttributeError or TypeError surface gives a better diagnostic than a boolean.

It is like judging whether a shop sells bread by whether the assistant comes back empty-handed: you cannot tell "we do not stock it" from "the storeroom door is jammed".

saying these in an interview costs you the question

  • Thinks hasattr only inspects the class dictionary
  • Believes hasattr never executes a property getter
  • Says hasattr swallows every exception, as on Python 2
  • Assumes a False result proves the attribute is undefined
  • Treats a passing hasattr check as making the next access safe
  • Uses hasattr then the attribute, paying for two lookups

context