繁体   English   中英

为 Python 中的 class 提供可选功能的最佳实践

[英]Best practice for providing optional functions for a class in Python

目前我正在编写一个带有插件系统的 Python 程序。 要开发新插件,必须创建新的 class 并从基本插件 class 继承。 现在应该可以通过 mixins 添加可选功能。 一些 mixin 提供了新的功能,其他的可以访问基本 class 的内置类型,并且可以使用它们或更改它们。

下面是一个简化的结构:

import abc
import threading

class Base:
    def __init__(self):
        self.config = dict()

        if hasattr(self, "edit_config"):
            self.edit_config()

     def start(self):
        """Starts the Plugin"""
        if hasattr(self, "loop"):
            self._loop()

class AMixin:
    def edit_config(self):
        self.config["foo"] = 123

class BMixin(abc.ABC):
    def _loop(self):
        thread = threading.Thread(target=self.loop, daemon=True)
        thread.start()

    @abc.abstractmethod
    def loop(self):
        """Override this method with a while true loop to establish a ongoing loop
        """
        pass

class NewPlugin(Base, AMixin, BMixin):
    def loop(self):
        while True:
            print("Hello")

 plugin = NewPlugin()
 plugin.start()

解决这个问题的最佳方法是什么?

编辑:我需要让我的问题更具体。 问题是上述是否是Pythonic方式,是否可以确保mixin与Base class一起专门继承。 此外,在访问基本 class 的内置类型时,如 VSCode 等 IDE 中获得对自动完成的支持会很好,就像在 AMixin 中一样,当然无需从它继承。

如果您想允许但不要求子类在基 class 调用的方法中定义某些行为,最简单的方法是在基 class 中声明该方法,有一个空实现,然后无条件地调用该方法。 这样您就不必在调用它之前检查该方法是否存在。

class Base:
    def __init__(self):
        self.config = dict()
        self.edit_config()
    
    def start(self):
        self.loop()
    
    def edit_config(self):
        pass
    
    def loop(self):
        pass

class AMixin:
    def edit_config(self):
        self.config["foo"] = 123

class NewPlugin(AMixin, Base):
    def loop(self):
        for i in range(10):
            print("Hello")

请注意,您必须在超类列表中的Base之前编写AMixin ,以便其edit_config方法覆盖来自Base的方法,而不是相反。 您可以通过编写class AMixin(Base):来避免这种情况,以便AMixin.edit_config始终在方法解析顺序中覆盖Base.edit_config

如果您想要求子类实现其中一种方法,则可以raise TypeError()而不是pass基类的方法。

我会将对 mix-ins 提供的方法的调用移至这些类定义的__init__方法。

import abc
import threading


class Base:
    def __init__(self, **kwargs):
        super.__init__(**kwargs)
        self.config = dict()


class AMixin:
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.edit_config()

    def edit_config(self):
        self.config["foo"] = 123


class BMixin(abc.ABC):
    def __init__(self, **kwargs):
        super().__init__(**kwargs):
        self.loop()

    def _loop(self):
        thread = threading.Thread(target=self.loop, daemon=True)
        thread.start()

    @abc.abstractmethod
    def loop(self):
        """Override this method with a while true loop to establish a ongoing loop
        """
        pass

class NewPlugin(Base, AMixin, BMixin):
    pass

当您实例化NewPlugin的具体子类时, Base.__init__AMixin.__init__BMixin.__init__将按此顺序调用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM