skip to content

What does ABCMeta.register give a virtual subclass, and what does it never provide?

level: seniorimportance: should knowfreq 32%

answer

  1. It answers one question and only one
  2. The class hierarchy is not touched
  3. isinstance says yes, the methods may be absent
  4. No MRO entry, no mixins, no enforcement
  5. Used to retrofit classes you do not own

basics

~20 s

Registration makes isinstance and issubclass answer True for a class that never inherited. It changes nothing else: the registered class keeps its own MRO, inherits no methods or defaults from the ABC, and its abstract methods are never verified.

solid answer

~50 s

`SomeABC.register(cls)` records `cls` as a **virtual subclass**, so `isinstance` and `issubclass` say yes; it returns the class, so it also reads as a decorator. Nothing else happens. `cls.__mro__` is untouched, so no concrete or mixin methods, class attributes or `__init__` are inherited, `super()` has no link to follow, and — the sharp edge — the ABC's abstract methods are never checked, so a class missing every one of them registers happily. That gap is the production hazard: after registration `isinstance` is an assertion by the author, not proof that the methods exist, so code that follows the check with a call to an ABC-supplied default gets `AttributeError`. Registration is right when you do not own the class; `collections.abc` uses it to make built-ins answer the checks. It cannot be undone, and `abc.get_cache_token` exists because each registration invalidates the metaclass's cached subclass checks.

code

python · 17 lines
python
from abc import ABC, abstractmethod

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

    def rollback(self):
        return "restore the last checkpoint"

@CatalogueLoader.register
class VendorLoader:
    pass

loader = VendorLoader()
print(isinstance(loader, CatalogueLoader))
print(VendorLoader.__mro__)
print(hasattr(loader, "rollback"), hasattr(loader, "load"))

go deeper

for a junior

Know that a class can pass an isinstance check against an abstract base without inheriting from it, and that the mechanism is an explicit register call somewhere in the code. Being able to spot that call is enough at this level.

for a middle

Explain the split precisely: registration affects isinstance and issubclass only, leaving the MRO, inherited methods and abstract-method enforcement untouched. Mention that collections.abc uses it to make built-in types answer the checks.

for a senior

Demonstrate the operational consequence. After registration isinstance is a claim rather than a proof, so a rollback or cleanup path that calls a default the ABC provides will fail with AttributeError on registered classes — the kind of bug that only fires on a rare error path.

for a principal

Own the policy: whether ABCs in your code base may carry concrete behaviour at all if registration is in use, whether libraries may register types they do not own on import, and when a real adapter class is worth its cost against a global, irreversible assertion.

### What registration is `ABCMeta.register(subclass)` records `subclass` in a set of *virtual subclasses* kept on the ABC. It returns the class, so it reads naturally as a decorator. After registration, `issubclass(VendorLoader, CatalogueLoader)` and `isinstance(VendorLoader(), CatalogueLoader)` are `True`. That is the entire feature. Registration is an assertion made to the type-checking questions `isinstance` and `issubclass`, and to nothing else. ### What it does not change * **The MRO.** `VendorLoader.__mro__` stays `(VendorLoader, object)`. The ABC is not a base class. * **Inherited behaviour.** No concrete methods, no mixin methods, no class attributes, no `__init__` come across. If `CatalogueLoader` defines a default `rollback()`, the registered class does not have it — `hasattr(loader, "rollback")` is `False`. * **`super()`.** There is no link to cooperate with; a registered class cannot call up into the ABC. * **Abstract-method enforcement.** Nothing is verified. `VendorLoader` need not define `load` at all, and registering it succeeds anyway. `__abstractmethods__` belongs to real subclasses. The gap between "isinstance says yes" and "the object has the methods" is exactly where this bites in production. ### The failure this produces Take a museum-catalogue importer that fans work out across a 17-service dependency graph and dispatches on type: `if isinstance(obj, CatalogueLoader): ...`. Someone registers a vendor-supplied loader class they cannot edit, because that is the sanctioned way to make third-party types answer the check. Imports run fine for months. Then a batch fails halfway and the importer enters its partial-failure rollback path, which calls `loader.rollback()` — the default the ABC provides and every real subclass inherits for free. The registered class never inherited it. The rollback dies with `AttributeError` in the middle of undoing work, in the one code path least exercised by tests, and the catalogue is left half-imported. The lesson is not "never register". It is that registration promises *classification*, so an ABC that also ships behaviour is only half-usable through it. If you rely on `register`, keep the ABC's concrete surface empty (or verify explicitly at registration), and treat `isinstance` as "the author asserted this fits", not "these methods exist". ### The rules and edge cases worth knowing * **Only classes.** `CatalogueLoader.register(42)` raises `TypeError: Can only register classes`. * **No cycles.** Registering an ancestor with a descendant raises `RuntimeError: Refusing to create an inheritance cycle`. * **No unregister.** There is no public API to undo it; a registration lasts for the life of the process. * **Caching.** `ABCMeta` caches subclass checks aggressively, including negative results. A global counter, readable via `abc.get_cache_token`, is bumped on every registration so those caches are invalidated — which is why a class registered *after* a failed `isinstance` check still starts answering `True`. Anything that caches its own `isinstance` results should compare cache tokens the same way. * **It composes with the hook.** A `__subclasshook__` on the ABC is consulted first; explicit registration is checked when the hook declines to decide. ### When it is the right tool Registration exists because Python's own type hierarchy could not be retrofitted. `collections.abc` registers built-ins so that `isinstance({}, collections.abc.MutableMapping)` is `True` without touching `dict`'s bases. Reach for it in the same situation: you do not own the class, you cannot add a base, or adding one would drag in a metaclass conflict or mixin behaviour you do not want. A dedicated adapter class that *really* inherits from the ABC and wraps the third-party object is the alternative, and it costs one object per instance while restoring the guarantees. ### How to answer Lead with the split: `register` buys you `isinstance`/`issubclass` and buys you nothing else — no MRO entry, no inherited methods, no `super()`, no verification that abstract methods exist. Then name the operational consequence: after registration, `isinstance` is a claim rather than a proof, so any code that follows the check with a call to an ABC-provided default is one registration away from an `AttributeError`. Mention the cache token if you want to show you have read the module — it is the detail that explains why registration order does not matter.

  • Can a registration be undone?
    Not through any public API. Once a class is registered with an ABC it stays registered for the life of the process, and there is no `unregister`. Design accordingly: registration is a permanent, global assertion about a class, so a library that registers third-party types on import is making that decision for every consumer in the interpreter.
  • Does register verify that the class implements the ABC's abstract methods?
    No, not one of them. `__abstractmethods__` is computed for real subclasses at class-creation time; a virtual subclass never goes through that path, so a class with none of the required methods registers without complaint. If you need a guarantee, verify explicitly at registration time or give the ABC a `__subclasshook__` that checks for the methods it cares about.
  • When would you prefer register over simply subclassing the ABC?
    When you cannot add a base: a third-party or built-in class, a class whose metaclass would conflict with ABCMeta, or a case where inheriting would drag in mixin behaviour you do not want. The alternative that keeps the guarantees is a thin adapter that genuinely subclasses the ABC and wraps the foreign object — one extra object per instance, but real inheritance.

Registration is a membership badge issued on the applicant's word: the door staff accept it, but nobody checked whether the holder can actually do the job, and the club's tools stay behind the members-only door.

saying these in an interview costs you the question

  • Thinks register inserts the ABC into the MRO
  • Expects a registered class to inherit mixin or default methods
  • Believes register enforces the ABC's abstract methods
  • Treats isinstance after registration as proof the methods exist
  • Claims a class can be unregistered later
  • Says super() can reach the ABC from a registered class

context