如何使用基于数据并在编译时检查的函数扩展 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 定义时完成此操作。
我有一些 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 定义时完成此操作。