如何使用基于数据并在编译时检查的函数扩展 class?

How to extend a class with functions based on data and checked at compile time?

我有一些 classes 的方法是由数据生成的。这些 class 可供用户使用,我希望在 class 实例化时进行类型检查。因此,如果用户没有正确实现这些 classes,则会在 class 构建时发生错误。也就是说,我想编写一个高阶函数来扩展 class' 签名并将这些方法标记为抽象方法。

因此,使用常规代码,实现将如下所示:

from abc import abstractmethod
class A():
  @abstractmethod
  def process_init(arg):
    pass

和用户class

class B(A):
  def process_init(arg):
    print(arg)

简单。除了在我的例子中 'init' 实际上是数据,并且可能是一长串原子。现在,文件的代码生成在 python 3 中很难看。所以我想做类似的事情:

class A():
    def dummy(arg):
        pass

    for i in ['init', 'start', 'pause', 'stop']:
        name = 'process_' + i
        setattr(A, name, dummy)
        make_abstract_method(A, name)

因为抽象方法没有实现,所以在实例化 B 时以下将反对

from a import A
class B(A):
    pass
B()

make_abstract_method的魔法咒语是什么? (我想要一个实例化时间错误,而不是运行时错误或异常,所以让虚拟执行 raise NotImplementedError 在这里不起作用。)

使用 abc 没有什么好的方法可以准确地完成您想要的。 abc 在 Python 中非常有限。从根本上说,它要求在实例化 class 对象时标记 abstractmethod。因此,您 可以 依赖 class 定义语句中修改 locals() 的未定义行为:

from abc import abstractmethod, ABCMeta
class A(metaclass=ABCMeta):
    def dummy(arg):
        pass

    for i in ['init', 'start', 'pause', 'stop']:
        name = 'process_' + i
        locals()[name] = abstractmethod(dummy) 

理想情况下,您希望能够做到的地方:

from abc import abstractmethod, ABCMeta
class A(metaclass=ABCMeta):
    def dummy(arg):
        pass

for i in ['init', 'start', 'pause', 'stop']:
    name = 'process_' + i
    setattr(A, name, abstractmethod(A.dummy))

但是抽象方法将不会被强制执行。

可能,您最好直接调用 type 构造函数(在这种情况下,当然是 type subclass ABCMeta) :

A = ABCMeta(
    'A',
    (object,),
    {
        f"process_{i}": abstractmethod(dummy)
        for i in ['init', 'start', 'pause', 'stop']
    }
)

当然,您无法在 编译 时完成任何这些操作,对于 Python 而言,编译只是将源代码转换为字节码,并进行一些小的优化,没有类型检查。

现在,如果您尝试实例化 B 对象,它将抛出运行时错误:

class B(A):
    pass

B() # TypeError: Can't instantiate abstract class B with abstract methods dummy, process_init, process_pause, process_start, process_stop

但是 abc 不会在 class 定义时间抱怨(无论如何这是你所能希望的最好的结果)。

您可以编写自定义元class 以在 class 定义时完成此操作。