I would like to wrap an abstractmethod
for all subclasses of an abc. I tried doing that by implementing __init_subclass__
as below:
import abc
class Base(abc.ABC):
@abc.abstractmethod
def foo(self) -> str:
pass
def __init_subclass__(cls):
super().__init_subclass__()
orig_foo = cls.foo
cls.foo = lambda s: orig_foo(s) + 'def'
class Derived(Base):
def foo(self):
return 'abc'
This works and if I do something like:
derived = Derived()
derived.foo() # -> 'abcdef'
which is expected. Unfortunately, I noticed that this approach does not invoke the abc check, so if I forget to implement foo
on Derived
:
class Derived(Base):
pass
I can still create it:
derived = Derived() # This works
derived.foo() # -> TypeError: unsupported operand type(s) for +: 'NoneType' and 'str'
Is there a way to do the above wrapping, but not break abc.abstractmethod
checks?
I was able to solve it by overriding cls.foo
in __init_subclass__
only if cls.foo
differs from Base.foo
(i.e. is already overridden).
This yields the following Base
implementation:
class Base(abc.ABC):
@abc.abstractmethod
def foo(self) -> str:
pass
def __init_subclass__(cls):
super().__init_subclass__()
orig_foo = cls.foo
if orig_foo != Base.foo: # Already overridden.
cls.foo = lambda s: orig_foo(s) + 'def'