Why does assigning `obj.__len__ = lambda: 5` leave `len(obj)` raising TypeError?
answer
- The assignment succeeded; the lookup missed it
- Two different paths for one name
- Builtins consult the type's MRO only
- Class-level assignment refreshes the type slot
- Vary state, not the dunder, per object
basics
~20 sBecause len() resolves len on type(obj), not on the object. The lambda lands in the instance dictionary, which implicit special method lookup never reads, so the class still has no len and len() raises TypeError.
solid answer
~40 s`len()` performs implicit special method lookup: it searches the MRO of `type(obj)` for `__len__` and calls it with the instance, without ever consulting `obj.__dict__` or the `__getattribute__`/`__getattr__` hooks. The assignment succeeds and `obj.__len__()` written explicitly will even return 5, because that is an ordinary attribute access — but the class is still lengthless, so `len(obj)` raises `TypeError: object of type 'X' has no len()`. CPython also caches each protocol in a C-level slot on the type when the class is created, so there is nowhere for a per-instance entry to be seen. Assigning on the *class* does work, immediately and for objects that already exist, because `type.__setattr__` refreshes those slots. Per-object behaviour has to come from one class-level `__len__` reading instance state.
code
python · 14 linesclass Box:
def __init__(self, n):
self._n = n
b = Box(3)
b.__len__ = lambda: 3
try:
len(b)
except TypeError as exc:
print(exc) # object of type 'Box' has no len()
Box.__len__ = lambda self: self._n # class-level assignment fills the slot
print(len(b)) # 3 — the existing instance sees it toogo deeper
Remember the outcome: attaching a special method to one object never changes what the builtins do. Put __len__ in the class body and give the class the state it needs.
Explain both paths and their consequences — the lambda really is in obj.__dict__, obj.__len__() really returns 5, the type's slot is what len() reads, and class-level assignment is retroactive. Name the class-level-dispatch fix.
Show the operational angle: patching a class to add a protocol is a process-wide change other code sees, and per-object protocol behaviour needs a synthesized subclass — the technique test-double libraries use — with the cost that implies.
Own the design position: protocols are contracts of a type, and code that wants to vary them per object is usually asking for a different type. Argue when a per-instance subclass is acceptable and when the design should change instead.
The assignment is not rejected and nothing is silently dropped — the lambda genuinely lands in `obj.__dict__`. It simply lives on the wrong side of a boundary the object model draws. ## Two lookups, one name `obj.__len__` is an ordinary attribute access: `object.__getattribute__` checks the type's MRO for a data descriptor, then the instance dictionary, then the type again for a plain class attribute, then `__getattr__`. Because the instance dictionary is checked before plain class attributes, an assigned lambda even shadows a class-level `__len__` for explicit access. `len(obj)` does something else entirely. It uses *implicit special method lookup*: the interpreter goes straight to the MRO of `type(obj)`, applies the descriptor protocol to what it finds, and calls it with the instance as the first argument. The instance dictionary is not on that route, and neither is `__getattribute__` or `__getattr__`. With no `__len__` anywhere in the class's MRO, the builtin reports `TypeError: object of type 'Box' has no len()`. ```python class Box: pass b = Box() b.__len__ = lambda: 5 print(b.__len__()) # 5 — ordinary attribute lookup len(b) # TypeError: object of type 'Box' has no len() ``` Notice the two calls also disagree about `self`: the explicit call is a plain function fetched from the instance dictionary, so it receives no instance and a zero-argument lambda works. A class-level `__len__` is a descriptor that binds, so it must accept `self`. Candidates often spot the missing `self` and stop there — fixing the signature changes nothing while the function is still on the instance. ## The slot layer Under CPython the reason is mechanical. When a class is created, its dunders are installed into C-level slots on the type object — a length slot, an iteration slot, a string slot, the number slots and so on — and `len()` reads that slot rather than performing a dictionary lookup. `type.__setattr__` keeps the slots in sync: assigning `Box.__len__` after the fact runs the slot fix-up and invalidates the type's version tag, so the change is visible to instances created before it. There is no equivalent hook for an instance, and there could not be a cheap one: honouring per-object protocols would force every operator to probe an instance dictionary, and any object that happened to carry an attribute named `__len__` would rewrite the semantics of the language for itself. ```python Box.__len__ = lambda self: 5 # class-level: fills the slot print(len(b)) # 5, even for the pre-existing instance ``` Since 3.11 the specializing adaptive interpreter (PEP 659) also caches resolved special methods per call site, keyed on the type's version tag — another layer that exists only because protocols are a property of the type. The behaviour is unchanged in 3.14. ## Getting per-object behaviour anyway There are exactly three honest options. **Put the variation inside one class-level dunder.** This is almost always the right answer: define `__len__` once on the class and have it read an instance attribute. Objects then differ in *state*, which is the mechanism the language supports. ```python class Bag: def __init__(self, items): self._items = list(items) def __len__(self): return len(self._items) ``` **Give the object its own class.** If the behaviour genuinely differs per object rather than per value, synthesize a small subclass for that object and set the dunder there. This is what `unittest.mock` does: configuring a magic method on a mock works because mock gives every mock instance a private subclass and sets the attribute on *that type*, and `MagicMock` ships with the magic methods preconfigured while a plain `Mock` does not support them. It costs a class object per instance, so it is a tool for test doubles and proxies, not for bulk data objects. **Patch the class, accepting that it is global.** A class-level assignment affects every instance everywhere, including objects other code is holding. That is fine for a deliberate, well-scoped monkeypatch and dangerous as a way to configure one object. ## What the interviewer is checking Whether you can distinguish the two lookup paths and name the consequence rather than just recite "it doesn't work". The strong answer says where the lambda actually went, why `obj.__len__()` still returns 5 while `len(obj)` fails, that class-level assignment succeeds retroactively, and what pattern to use instead. The weak answer blames the missing `self` parameter, or claims Python refuses to assign dunders on instances at all.
- Does assigning `__len__` on the class instead take effect for objects that already exist?Yes, immediately. The lookup consults the type on every call, and `type.__setattr__` refreshes the type's internal slot and invalidates its version tag, so previously created instances get the new behaviour with no re-instantiation. That is also why class-level monkeypatching of protocols is global and hard to scope.
- How does `unittest.mock` let you configure a magic method on a single mock object then?It sidesteps the rule rather than breaking it: each mock instance gets its own private subclass, and configuring a magic method sets it on that type. `MagicMock` arrives with the magic methods already configured, while a plain `Mock` does not support them — the difference is exactly this lookup rule.
- Why does `b.__len__()` return 5 while `len(b)` fails, if both name the same attribute?They use different lookups. The explicit call is ordinary attribute access, which checks the instance dictionary and finds the lambda; the builtin uses implicit special method lookup, which reads only the type's MRO. The explicit call also passes no instance, which is why a zero-argument lambda works there and would fail as a class attribute.
saying these in an interview costs you the question
- Blames the missing self parameter rather than the lookup rule
- Says Python forbids assigning dunder names on instances
- Claims a class-level __getattr__ would rescue len(obj)
- Thinks patching the class only affects instances created later
- Believes obj.__len__() and len(obj) must resolve to the same object