Virtual Subclasses and register()
Свайпніть щоб показати меню
ABCs can recognize classes as subclasses even when those classes do not inherit from the ABC. This is called virtual subclassing – the class is registered with the ABC and passes isinstance() and issubclass() checks without any inheritance relationship.
The Problem register() Solves
Sometimes you want a class to satisfy an ABC interface without modifying its source code. This is common when integrating third-party classes or built-in types into your own ABC hierarchy:
1234567891011121314from abc import ABC, abstractmethod class Serializable(ABC): @abstractmethod def serialize(self): pass # A third-party class you cannot modify class ExternalReport: def serialize(self): return {"type": "external", "data": "..."} # Without register() – isinstance fails print(isinstance(ExternalReport(), Serializable)) # False
Using register()
ABC.register(subclass) marks a class as a virtual subclass. It passes isinstance() and issubclass() checks without inheriting:
12345678910111213141516171819from abc import ABC, abstractmethod class Serializable(ABC): @abstractmethod def serialize(self): pass class ExternalReport: def serialize(self): return {"type": "external", "data": "..."} # Registering the external class as a virtual subclass Serializable.register(ExternalReport) print(isinstance(ExternalReport(), Serializable)) # True print(issubclass(ExternalReport, Serializable)) # True # But it is NOT in the MRO print(Serializable in ExternalReport.__mro__) # False
register() as a Decorator
register() can also be used as a class decorator:
123456789101112131415161718from abc import ABC, abstractmethod class Cacheable(ABC): @abstractmethod def cache_key(self): pass @Cacheable.register class UserSession: def __init__(self, session_id): self.session_id = session_id def cache_key(self): return f"session:{self.session_id}" session = UserSession("sess-001") print(isinstance(session, Cacheable)) # True print(session.cache_key()) # "session:sess-001"
No Enforcement for Virtual Subclasses
The critical difference between real and virtual subclasses: register() does not enforce abstract method implementation. A virtual subclass that is missing required methods will not raise TypeError on instantiation:
1234567891011121314151617from abc import ABC, abstractmethod class Exportable(ABC): @abstractmethod def export(self): pass class BrokenExporter: pass # Missing export() Exportable.register(BrokenExporter) # No TypeError – virtual subclasses bypass enforcement broken = BrokenExporter() print(isinstance(broken, Exportable)) # True – but export() is missing! broken.export() # AttributeError at call time
Use register() when you are confident the class satisfies the interface. Prefer real inheritance when you want enforcement.
Real vs Virtual Subclasses
Дякуємо за ваш відгук!
Запитати АІ
Запитати АІ
Запитайте про що завгодно або спробуйте одне із запропонованих запитань, щоб почати наш чат