使用 __init_subclass__ 修补抽象方法,同时保留 abc 检查

Use __init_subclass__ to patch abstract methods while preserving the abc check

我想为 abc 的所有子类包装一个 abstractmethod。我尝试通过如下实施 __init_subclass__ 来做到这一点:

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'

这有效,如果我做类似的事情:

derived = Derived()
derived.foo()  # -> 'abcdef'

这是预期的。不幸的是,我注意到这种方法不会调用 abc 检查,所以如果我忘记在 Derived:

上实现 foo
class Derived(Base):
  pass

我还能创造它:

derived = Derived()  # This works
derived.foo()        # -> TypeError: unsupported operand type(s) for +: 'NoneType' and 'str'

有没有办法进行上述包装,但又不破坏 abc.abstractmethod 检查?

只有当 cls.foo不同于Base.foo(即已被覆盖)。

这会产生以下 Base 实现:

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'

我认为更好的技术是声明一个单独的 non-abstract 使用抽象方法的方法。

导入 abc

class Base(abc.ABC):

  @abc.abstractmethod
  def foo_body(self) -> str:
      pass

  def foo(self) -> str:
    return self.foo_body() + 'def'


class Derived(Base):

  def foo_body(self):
    return 'abc'

具体的子类只负责重写foo_bodyBase.foo本身就不用动了