【发布时间】:2021-03-03 04:03:08
【问题描述】:
我正在构建用于动态加载模块的基础架构。每个模块都应该包含一个继承自同一个基类的类。
模块的结构是这样的:
app
\- vendors
\- __init__.py
\- vendor_base.py
\- vendor1.py
\- vendor2.py
...
__init__.py:
from .vendor_base import VendorBase
self = sys.modules[__name__]
__all__ = []
vendors = []
modules = [ f
for f in glob.glob(join(dirname(__file__), "*.py"))
if isfile(f) and not f.endswith('__init__.py')
]
for module in modules:
with open(module, 'rb') as fp:
module_name = splitext(basename(module))[0]
ma = imp.load_module(
'app.vendors.' + module_name,
fp, basename(module), ('.py', 'r', imp.PY_SOURCE))
classes = { c for c in ma.__dict__.items()
if isinstance(c[1], type) and issubclass(c[1], VendorBase) }
for class_pair in classes:
setattr(self, class_pair[0], class_pair[1])
if class_pair[0] not in __all__:
__all__.append(class_pair[0])
if issubclass(class_pair[1], VendorBase):
vendors.append(class_pair[1])
vendor_base.py:
from abc import ABCMeta, abstractmethod
class VendorBase(metaclass=ABCMeta):
@classmethod
def __subclasshook__(cls, subclass):
return subclass is not cls and cls in subclass.__mro__
@abstractmethod
def post_purchase_order(self, purchase_order):
pass
vendor1.py:
from . import VendorBase
class Vendor1(VendorBase):
def post_purchase_order(self, purchase_order):
pass
vendor2.py:
from . import VendorBase
class Vendor2(VendorBase):
def post_purchase_order(self, purchase_order):
pass
所以我希望类 Vendor1 和 Vendor2 被加载并在模块 app.vendors 中可用(在 __all__ 和 vendors 中)。但是它们没有加载。我检查了 VendorBase.__subclasshook__() 中的类比较没有按预期工作。
当我运行issubclass(VendorBase, Vendor1) 时,我得到False。此外,即使在调试器VendorBase.__subclasshook__(cls, subclass) 中:
cls
'app.vendors.vendor_base.VendorBase'>
子类
'app.vendors.vendor_base.VendorBase'>
我检查cls is subclass 我仍然得到False。
我虽然它可能与供应商类模块中不同的导入基类名称有关。所以我确保VendorBase 是从vendors 导入的,它也被使用。但似乎仍然有不同的类实例。
【问题讨论】:
-
您正在寻找
isinstance()。 Python 中的is是身份检查;1 is int返回False -
您可能只想使用入口点而不是滚动您自己的加载程序...packaging.python.org/guides/creating-and-discovering-plugins/…
-
回复
issubclass(VendorBase, Vendor1)。你的论点倒过来了。 -
@Flair 我相信
isinstance()适用于对象。我在比较类(不是对象) -
@AKX 每次添加插件时不需要重新运行设置吗?这个想法是,只要将文件放入目标目录,就会加载新模块