Reputation: 93
I'm wondering how best I can define __subclasshook__
on a hierarchy of abstract classes in Python. Let's say I have an interface, and an ABC that inherits from that interface. Is something like this okay?
import abc
from typing import IO
class IFileHandler(metaclass=abc.ABCMeta):
def import(self) -> bytes:
raise NotImplementedError
def export(self) -> IO:
raise NotImplementedError
@classmethod
def __subclasshook__(cls, subclass):
return all((
hastattr(subclass, 'import'),
callable(getattr(subclass, 'import', None)),
hasattr(subclass, 'export'),
callable(getattr(subclass, 'export', None))
)) or NotImplemented
class FileBase(IFileHandler, metaclass=abc.ABCMeta):
def __init__(self):
...
def import(self):
...
def export(self):
raise NotImplementedError
def another_method(self):
...
@classmethod
def __subclasshook__(cls, subclass):
if super().__subclasshook(subclass) is NotImplemented:
return NotImplemented
return all((
hasattr(subclass, '__init__'),
callable(getattr(subclass, '__init__', None)),
hasattr(subclass, 'another_method'),
callable(getattr(subclass, 'another_method', None)),
)) or NotImplemented
class ConcreteFileClass(FileBase):
def export(self):
...
Is there a more graceful way for calling the superclass' __subclasshook__
and dealing with its result? Have I barked up completely the wrong tree here? Any insight would be much appreciated, thanks!
Upvotes: 2
Views: 372
Reputation: 114330
You might consider using the abstractmethod
decorator instead of reinventing the whole machinery of abc
:
from abc import ABCMeta, abstractmethod
from typing import IO
class IFileHandler(metaclass=ABCMeta):
@abstractmethod
def import(self) -> bytes:
pass
@abstractmethod
def export(self) -> IO:
pass
class FileBase(IFileHandler):
@abstractmethod
def __init__(self):
pass
def import(self):
...
def export(self):
...
@abstractmethod
def another_method(self):
pass
You don't need to explicitly supply metaclass
to FileBase
, since inheritance already takes care of it. abstractmethod
will not allow a subclass to be instantiated without a concrete implementation. Rather than raising NotImplementedError
in your stub methods, use Ellipsis
(...
) or pass
. That way, subclasses will be able to correctly call super().whatever
without errors.
Upvotes: 2