skip to content

Why does instantiating a subclass of abc.ABC that leaves an @abstractmethod unimplemented raise TypeError?

level: juniorimportance: must knowfreq 65%

answer

  1. Enforcement lives in the metaclass
  2. A frozenset computed at class creation
  3. Construction refuses, definition succeeds
  4. __abstractmethods__ must be empty to instantiate
  5. The decorator only sets a flag

basics

~20 s

abc.ABCMeta collects every still-abstract name into a frozenset stored on the class as abstractmethods, and object construction refuses to run while that set is non-empty. The failure therefore lands at instantiation, not at class definition.

solid answer

~40 s

`@abc.abstractmethod` only sets `__isabstractmethod__ = True` on the function; the enforcement is in the metaclass. When `ABCMeta` builds a class it collects the names that are still abstract — from the namespace and from each base's set — into `__abstractmethods__`, and `object.__new__` refuses to construct an instance while that frozenset is non-empty, raising `TypeError: Can't instantiate abstract class PartialImporter without an implementation for abstract method 'rollback'`. Two consequences matter in review. Defining the incomplete subclass **succeeds**: the module imports cleanly and only the call fails, so a missing method is a first-construction runtime error. And the check is name-based, not signature-based, so binding the name to anything — even `load = None` — clears it. On a class that does not go through `ABCMeta`, `@abstractmethod` is inert decoration and instances are created happily.

code

python · 19 lines
python
from abc import ABC, abstractmethod

class CatalogueImporter(ABC):
    @abstractmethod
    def load(self, record): ...

    @abstractmethod
    def rollback(self): ...

class PartialImporter(CatalogueImporter):
    def load(self, record):
        return record

print(PartialImporter.__abstractmethods__)

try:
    PartialImporter()
except TypeError as exc:
    print(exc)

go deeper

for a junior

Be ready to recall the sequence in one breath: decorate with @abstractmethod, inherit from abc.ABC, and the TypeError appears when someone tries to create the object. Knowing that the class definition itself succeeds is the part that separates a memorised answer from an understood one.

for a middle

Explain the mechanics: the decorator sets a flag on the function, ABCMeta gathers the still-flagged names into abstractmethods at class creation, and object.new checks that set. Mention that the check is name presence only, so signatures are never verified.

for a senior

Show you have debugged it in production. The failure is a first-construction runtime error, not an import error, so it can survive a deploy and surface on a rare code path; and an ABC guarantees only that a name exists, which is why review, not the machinery, catches wrong signatures.

for a principal

Own the question of whether nominal ABCs are the right contract mechanism for your code base at all, given that they enforce names rather than behaviour and add a metaclass to every subclass. Be able to say when a plain convention plus static checking buys more than runtime enforcement.

### The decorator does almost nothing `abc.abstractmethod` is three lines of Python. It sets `__isabstractmethod__ = True` on the function object handed to it and returns that same function unchanged. It does not wrap the function, does not replace its body, and does not block anything by itself. An abstract method is an ordinary method carrying a flag; its body still runs if a subclass calls it through `super()`, which is why abstract methods in real code are often a useful default rather than `...`. ### The metaclass reads the flag The enforcement lives in `abc.ABCMeta`. Writing `class CatalogueImporter(ABC)` is simply the readable way to get that metaclass — `class CatalogueImporter(metaclass=ABCMeta)` builds exactly the same thing. When `ABCMeta` creates a class it does two passes. First it scans the new class namespace for names whose value has a truthy `__isabstractmethod__`. Then, for every base class that already has an `__abstractmethods__` set, it re-resolves each of those names with `getattr` on the new class and keeps the ones that are *still* abstract. The union is frozen and stored as a class attribute, `__abstractmethods__`. So `PartialImporter.__abstractmethods__` is `frozenset({'rollback'})`: `load` was resolved to a concrete function defined in the subclass and dropped out of the set, `rollback` resolved back to the flagged function on the base and stayed. ### Where the TypeError is raised `object.__new__` refuses to build an instance of a class whose `__abstractmethods__` is non-empty, and that is the whole enforcement: ``` TypeError: Can't instantiate abstract class PartialImporter without an implementation for abstract method 'rollback' ``` With several missing names the message pluralises and lists them all: `... without an implementation for abstract methods 'load', 'rollback'`. The timing is the part interviews probe. **Class creation succeeds.** The module imports cleanly, the incomplete class is a perfectly good object, you can subclass it further, and static analysis or an editor may flag nothing. Only the call `PartialImporter()` fails. A missing implementation is therefore a runtime error on the first construction — which in a service means the first request that reaches that code path, not startup, unless something constructs the object eagerly at import. ### The check is name presence, not a contract `ABCMeta` asks only whether the name resolves to something non-abstract. It never inspects a signature, a return type or a docstring. Consequences worth saying out loud: * `def load(self): ...` in the subclass satisfies the base's `load(self, record)` even though calling it will fail with a `TypeError` about arguments. * Binding the name to a non-function clears it too: `load = None` in the class body makes the class instantiable, and so does a class-level attribute or an assignment to a `lambda`. * An ABC with no abstract methods at all is instantiable. `class Marker(ABC): pass` builds instances happily; `ABC` itself is only "abstract" in the sense that it supplies the metaclass. * If a class decorator adds or removes methods *after* the class object exists, the frozenset is already computed and stale; `abc.update_abstractmethods` recomputes it. Structural checkers do enforce signatures, but that is a separate, static story — the ABC machinery at runtime is presence-only. ### The failure mode when ABCMeta is absent Decorating methods with `@abstractmethod` on a class that is **not** built by `ABCMeta` does nothing enforceable. There is no `__abstractmethods__` attribute, `object.__new__` has nothing to check, and instances are created without complaint. This is the most common way an intended interface silently stops being one: someone refactors the base class, drops the `ABC` base (or introduces a competing metaclass) and every `@abstractmethod` decoration downgrades to documentation. ### What to say in an interview State the three moving parts in order: the decorator sets a flag on the function; `ABCMeta` collects the still-flagged names into `__abstractmethods__` at class-creation time; `object.__new__` raises `TypeError` while that set is non-empty. Then add the two facts that show you have debugged it — the error is at instantiation, not definition, and the check is name-based, so an ABC guarantees that a name exists and nothing about what it accepts or returns. If you want a compile-time contract on argument types, an ABC is not the tool; if you want "this object cannot be built until someone supplies a rollback", it is exactly the tool.

  • Does the class definition itself fail, or only the call that creates the instance?
    Only the call. `ABCMeta` records the outstanding names in `__abstractmethods__` while creating the class and raises nothing; the class object is perfectly usable and can be subclassed further. `object.__new__` is what refuses, so the `TypeError` appears on the first construction — in a service that is the first request reaching that path, not startup.
  • How can a subclass clear the check without writing a real implementation?
    By binding the name to anything non-abstract. `load = None` in the class body, a class-level attribute, or a one-line `lambda` all satisfy `ABCMeta`, which only asks whether the name still resolves to something flagged abstract. Signatures are never inspected. If a class decorator injects methods after the class exists, the frozenset is stale and `abc.update_abstractmethods` recomputes it.
  • What does @abstractmethod do on a class whose metaclass is not ABCMeta?
    Nothing enforceable. It sets `__isabstractmethod__` on the function and stops there; without `ABCMeta` there is no `__abstractmethods__` attribute and `object.__new__` has nothing to check, so instances are created normally. This is how an intended interface quietly stops being one after someone drops the `abc.ABC` base during a refactor.

The decorator is a sticky note on a method; ABCMeta is the clerk who counts the notes when the class is filed, and object construction is the door that stays shut while any note is left.

saying these in an interview costs you the question

  • Says the TypeError is raised when the class is defined
  • Thinks @abstractmethod blocks instantiation without ABCMeta
  • Believes Python checks the abstract method's signature
  • Claims an ABC with no abstract methods cannot be instantiated
  • Treats abc.ABC as a keyword rather than a base supplying a metaclass

context