如何在python中发现特定包中的类?

2024-09-28 18:46:10 发布

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

我有一个插件式模块包。看起来像这样:

/Plugins 
/Plugins/__init__.py
/Plugins/Plugin1.py
/Plugins/Plugin2.py 
etc...

每个.py文件都包含一个从PluginBaseClass派生的类。所以我需要列出Plugins包中的每个模块,然后搜索实现PluginBaseClass的任何类。理想情况下,我希望能够做这样的事情:

^{pr2}$

我也看到了其他答案,但我的情况不同。我有一个到基本包的实际导入(即:import project.Plugins),我需要在发现模块之后找到类。在


Tags: 模块文件答案py插件initetc情况
3条回答

您可以(也可能应该)将__init__.py中的__all__定义为包中的子模块列表;这样就可以支持人们执行from Plugins import *。如果已经这样做了,那么可以使用

import Plugins
import sys
modules = { }
for module in Plugins.__all__:
    __import__( module )
    modules[ module ] = sys.modules[ module ]
    # iterate over dir( module ) as above

这里发布的另一个答案失败的原因是__import__导入了最低级别的模块,但是返回了顶层模块(参见docs)。我不知道为什么。在

扫描模块不是个好主意。如果需要类注册表,则应查看metaclasses或使用现有的解决方案,如zope.interface。 通过元类的简单解决方案可能如下所示:

from functools import reduce
class DerivationRegistry(type):
    def __init__(cls,name,bases,cls_dict):
        type.__init__(cls,name,bases,cls_dict)
        cls._subclasses = set()
        for base in bases:
            if isinstance(base,DerivationRegistry):
                base._subclasses.add(cls)

    def getSubclasses(cls):
        return reduce( set.union,
                       ( succ.getSubclasses() for succ  in cls._subclasses if isinstance(succ,DerivationRegistry)),
                       cls._subclasses)

class Base(object):
    __metaclass__ = DerivationRegistry

class Cls1(object):
    pass

class Cls2(Base):
    pass

class Cls3(Cls2,Cls1):
    pass

class Cls4(Cls3):
    pass

print(Base.getSubclasses())

编辑:这是一个修正的解决方案。我意识到我在测试上一个错误时犯了一个错误,它并没有像你期望的那样工作。所以这里有一个更完整的解决方案:

import os
from imp import find_module
from types import ModuleType, ClassType

def iter_plugins(package):
    """Receives package (as a string) and, for all of its contained modules,
    generates all classes that are subclasses of PluginBaseClass."""

    # Despite the function name, "find_module" will find the package
    # (the "filename" part of the return value will be None, in this case)
    filename, path, description = find_module(package)

    # dir(some_package) will not list the modules within the package,
    # so we explicitly look for files. If you need to recursively descend
    # a directory tree, you can adapt this to use os.walk instead of os.listdir
    modules =  sorted(set(i.partition('.')[0]
                          for i in os.listdir(path)
                          if i.endswith(('.py', '.pyc', '.pyo'))
                          and not i.startswith('__init__.py')))
    pkg = __import__(package, fromlist=modules)
    for m in modules:
        module = getattr(pkg, m)
        if type(module) == ModuleType:  
            for c in dir(module):
                klass = getattr(module, c)
                if (type(klass) == ClassType and
                    klass is not PluginBaseClass and
                    issubclass(klass, PluginBaseClass)):
                    yield klass

我以前的解决方案是:

你可以尝试一下:

^{pr2}$

实际上,更好的是,如果你想要一些模块化:

from types import ModuleType

def iter_plugins(package):
    # This assumes "package" is a package name.
    # If it's the package itself, you can remove this __import__
    pkg = __import__(package)
    for item in dir(pkg):
        module = getattr(pkg, item)
        if type(module) == ModuleType:  
            for c in dir(module):
                klass = getattr(module, c)
                if issubclass(klass, PluginBaseClass):
                    yield klass

相关问题 更多 >