继承非抽象 class 并将其所有功能转换为抽象方法

Inheriting a non-abstract class and turning all of its functions into abstractmethods

我得到了一个上游接口,它的所有功能都定义为非抽象的,而实际上它们应该用 @abstractmethod 装饰。当我在调用它时没有实现其中一个功能时,我想收到一个错误。为此,我将创建一个包装器 class 并手动遍历其定义的每个函数并执行如下操作:

from abc import ABC, abstractmethod

class Foo(object):
    def foo(self):
        print("Foo")

class AbstractFoo(Foo, ABC):
    @abstractmethod
    def foo(self):
        return super().foo()

class ConcreteFoo(AbstractFoo):
    def foo(self):
        print("Concrete Foo")
        super().foo()

f = ConcreteFoo()
f.foo()

输出:

Concrete Foo
Foo

我想要一些方法来对 Foo 定义的 所有 函数执行此操作。显然,像__str____repr__这样的继承魔法函数应该被适当转发。

有没有人知道一个很好的 pythonic 方式来做这件事?

def validate_base_class_implemntation(cls):
    base_cls_funcs = []
    for attr in cls.__bases__[0].__dict__:
        if callable(getattr(cls, attr)):
            base_cls_funcs.append(attr)

    cls_funcs = []
    for attr in cls.__dict__:
        if callable(getattr(cls, attr)):
            cls_funcs.append(attr)

    missing_funcs = [x for x in base_cls_funcs if x not in cls_funcs]

    if len(missing_funcs) > 0:
        print("Not implemented functions are: {}".format(','.join(missing_funcs)))
        raise Exception("Not implement function exception!")

    return cls


class Foo(object):
    def foo(self):
        print("Foo")

    def boo(self):
        print("Wow")


@validate_base_class_implemntation
class ConcreteFoo(Foo):
    def foo(self):
        print("Concrete Foo")
        super().foo()


f = ConcreteFoo()
f.foo()

100% 不确定这是否是您的意思。

此装饰器检查 class 装饰器是否实现了所有基本 class 功能(在您的情况下,它们没有用抽象装饰)。如果有一个你的装饰 class 没有实现的功能,它会引发异常。

可以修改原来的classFoo,把它的所有方法都抽象成抽象方法,然后用[=13=定义一个Foo的空白subclass ] 为了处理支票:

from abc import ABCMeta, abstractmethod
from types import FunctionType


class AbstractFoo(Foo, metaclass=ABCMeta):
    pass


names = set()
for k, v in vars(Foo).items():
    if k.startswith('__') and k.endswith('__'):
        continue
    elif isinstance(v, FunctionType):
        names.add(k)
        v.__isabstractmethod__ = True

AbstractFoo.__abstractmethods__ = frozenset(names)

旁注: 这种方法依赖于 abc 使用的 dunder 属性,因此可以在不弃用的情况下中断。