__subclasshook__ and Custom Membership
Pyyhkäise näyttääksesi valikon
register() requires explicit opt-in for each class. __subclasshook__ goes further – it lets an ABC automatically accept any class that satisfies a structural condition, without requiring registration at all. This is how Python's built-in ABCs like Iterable work.
How __subclasshook__ Works
__subclasshook__ is a class method called by isinstance() and issubclass() before consulting the MRO or the registered subclasses. It returns:
True– the class is considered a subclass;False– the class is explicitly not a subclass;NotImplemented– fall through to normal lookup.
1234567891011121314151617181920212223from abc import ABC, abstractmethod # ABC that automatically accepts any class with a serialize() method class Serializable(ABC): @abstractmethod def serialize(self): pass @classmethod def __subclasshook__(cls, subclass): if cls is Serializable: if any("serialize" in klass.__dict__ for klass in subclass.__mro__): return True return NotImplemented # A class that never heard of Serializable class TransactionRecord: def serialize(self): return {"id": "TX-001", "amount": 4500.0} # Passes isinstance() without register() or inheritance print(isinstance(TransactionRecord(), Serializable)) # True print(issubclass(TransactionRecord, Serializable)) # True
How the Standard Library Uses __subclasshook__
collections.abc.Iterable uses exactly this mechanism – any class that defines __iter__ is automatically considered an Iterable:
12345678910111213141516from collections.abc import Iterable # Custom class with __iter__ – no inheritance from Iterable class DataStream: def __init__(self, records): self._records = records def __iter__(self): return iter(self._records) stream = DataStream([{"id": 1}, {"id": 2}, {"id": 3}]) print(isinstance(stream, Iterable)) # True – __subclasshook__ recognized __iter__ for record in stream: print(record)
Combining __subclasshook__ with Abstract Methods
__subclasshook__ controls isinstance()/issubclass() checks, but does not enforce abstract methods on real subclasses. The two mechanisms are independent:
1234567891011121314151617181920212223242526from abc import ABC, abstractmethod class Runnable(ABC): @abstractmethod def run(self): pass @classmethod def __subclasshook__(cls, subclass): if cls is Runnable: if any("run" in klass.__dict__ for klass in subclass.__mro__): return True return NotImplemented # Structural check passes – run() is present class BackgroundJob: def run(self): print("Running background job") print(isinstance(BackgroundJob(), Runnable)) # True # Real subclass still gets enforcement class BrokenJob(Runnable): pass # Missing run() job = BrokenJob() # TypeError – abstract method not implemented
register() vs __subclasshook__
Kiitos palautteestasi!
Kysy tekoälyä
Kysy tekoälyä
Kysy mitä tahansa tai kokeile jotakin ehdotetuista kysymyksistä aloittaaksesi keskustelumme