为什么子类的实例化在__init_subclass__中工作,尽管它是一个缺少方法的抽象基类?



我正在尝试编写一个简单的插件系统。

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

最新更新