为 Python 中的 class 提供可选函数的最佳实践
Best practice for providing optional functions for a class in Python
目前我正在编写一个带有插件系统的Python程序。要开发一个新插件,必须创建一个新的 class 并从基础插件 class 继承。现在应该可以通过混合添加可选功能。一些 mixins 提供新功能,其他人访问基类的内置类型 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和Baseclass一起独占继承。此外,在 IDE 中获得对例如 VSCode 的支持会很好。访问 Base class 的内置类型时自动完成,就像在 AMixin 中一样,当然没有继承它。
我会将对混合插件提供的方法的调用移动到 类.
定义的 __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__
将按此顺序调用。
如果你想允许但不要求子classes在基class调用的方法中定义一些行为,最简单的方法是在基[=]中声明方法26=],有一个空的实现,只是无条件地调用这个方法。这样您就不必在调用之前检查该方法是否存在。
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")
注意你必须在superclass列表中的Base
之前写上AMixin
,这样它的edit_config
方法就会覆盖[=12]中的方法=],而不是相反。您可以通过编写 class AMixin(Base):
来避免这种情况,以便 AMixin.edit_config
始终覆盖方法解析顺序中的 Base.edit_config
。
如果你想要求子classes实现其中一个方法,那么你可以raise TypeError()
而不是基础class的方法中的pass
.
目前我正在编写一个带有插件系统的Python程序。要开发一个新插件,必须创建一个新的 class 并从基础插件 class 继承。现在应该可以通过混合添加可选功能。一些 mixins 提供新功能,其他人访问基类的内置类型 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和Baseclass一起独占继承。此外,在 IDE 中获得对例如 VSCode 的支持会很好。访问 Base class 的内置类型时自动完成,就像在 AMixin 中一样,当然没有继承它。
我会将对混合插件提供的方法的调用移动到 类.
定义的__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__
将按此顺序调用。
如果你想允许但不要求子classes在基class调用的方法中定义一些行为,最简单的方法是在基[=]中声明方法26=],有一个空的实现,只是无条件地调用这个方法。这样您就不必在调用之前检查该方法是否存在。
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")
注意你必须在superclass列表中的Base
之前写上AMixin
,这样它的edit_config
方法就会覆盖[=12]中的方法=],而不是相反。您可以通过编写 class AMixin(Base):
来避免这种情况,以便 AMixin.edit_config
始终覆盖方法解析顺序中的 Base.edit_config
。
如果你想要求子classes实现其中一个方法,那么你可以raise TypeError()
而不是基础class的方法中的pass
.