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

2024-04-26 13:29:15 发布

您现在位置:Python中文网/ 问答频道 /正文

我想写一个简单的插件系统。你知道吗

a)为此,我发现下面的代码使用了3.6魔术函数__init_subclass__https://stackoverflow.com/a/41924331/1506569,它改编自pep487,在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,它只是比较基类和子类上的函数对象是否不同(通过使用class和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()

这个版本是可行的,但对我来说似乎很复杂,似乎它可能会进一步打破线?你知道吗

有没有我错过的方法?为什么实例化可以工作? 抱歉,如果我错过了一个类似的问题,我试图寻找了很长一段时间。你知道吗


Tags: 插件initdefnotpluginsrenderplugindo
1条回答
网友
1楼 · 发布于 2024-04-26 13:29:15

如果您只想强制子类实现一个方法,@abstractmethod就足够了。你不需要init_subclass。您的代码是正确的,但是,您没有在任何地方实例化Render。在创建对象而不是类时,抽象方法会给您一个错误。如果你写Render(),你会得到一个错误

Can't instantiate abstract class Render with abstract methods do_something

相关问题 更多 >