我正在尝试编写一个简单的插件系统。
a) 为此,我找到了以下代码,使用了改编自 PEP 487 的 3.6 魔术函数__init_subclass__
https://stackoverflow.com/a/41924331/1506569,该函数在plugins
列表中的父类中注册每个定义类。
b) 我现在想"强制"插件的编写者实现一个特定的方法,该方法将由我的程序调用。理想情况下,这会在导入时警告用户,他的插件不起作用,但不会阻止程序运行。为此,我尝试将 ABC 作为父类添加到插件中并添加一个@abstractmethod
。
我最初对ABC和__init_subclass__
的尝试:
from abc import ABC, abstractmethod
class Plugin(ABC):
plugins = []
@classmethod
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
try:
__class__.plugins.append(cls()) # Does not raise exception
except Exception as e:
print("Warning: Your plugin might not work, missing abstract method")
@abstractmethod
def do_something(self):
...
class Render(Plugin):
def __init__(self):
print("Render init")
# does not implement 'do_something'
for plugin in Plugin.plugins:
plugin.do_something() # Calls 'do_something' on Render, without raising an exception
这里的问题是,实例化__class__.plugins.append(cls())
不会引发异常,而是将实例化的Render
对象添加到plugins
列表中。 调用函数Render.do_something
(底部的循环)也不会引发异常。它什么都不做。
我找到的解决方案描述在这里:https://stackoverflow.com/a/54545842/1506569 简单地比较基类和子类上的函数对象是否不同(通过使用类和 cls)。这使得从ABC继承变得不必要,并且似乎比需要的要复杂得多。
第二次尝试,没有ABC:
class Plugin():
plugins = []
@classmethod
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
if getattr(cls, 'do_something') is getattr(__class__, 'do_something'):
print("Warning: Your plugin might not work, missing 'do_something'")
else:
__class__.plugins.append(cls())
def do_something(self):
...
class Render(Plugin):
def __init__(self):
print("Render init")
for plugin in Plugin.plugins:
plugin.do_something()
这个版本有效,但对我来说似乎很复杂,看起来它可能会进一步崩溃?
有没有我错过的方法?为什么实例化有效? 抱歉,如果我错过了类似的问题,我尝试了很长时间。
如果你只想强制子类实现一个方法,@abstractmethod
就足够了。你不需要init_subclass
.您的代码是正确的,但是,您没有在任何地方实例化Render
。抽象方法在创建对象而不是类时会出错。如果你写Render()
,你实际上会得到一个错误
无法实例化抽象类 使用抽象方法呈现 do_something