Python组件扫描

2024-04-20 04:15:24 发布

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

我有一个叫@find_me的装饰师。我想找到所有用它装饰的类

或者,我有一个名为FindMe的类,我想找到它的所有子类

为什么??因为我想在它们被import-ed之前对这些类做一些事情

所以我读了关于__subclasses()__和关于查找装饰器的内容。 我发现的解决方案的问题是,必须在代码运行之前import-ed类

换句话说,如果我有:

  • 在模块${proj_root}/some_path/FindMe.pyaclass FindMe(object):
  • 在模块${proj_root}/some_other_path/NeedsToBeFound.py中,a class NeedsToBeFound(FindMe):
  • 和另一个模块${proj_root}/yet_another_path/some_module.py
  • 如果some_module.py看起来像:
import ... FindMe

...

subclasses_of_FindMe = FindMe.__subclasses()__

那么预期的类NeedsToBeFound将不会出现在结果中(假设在过程中的某个地方没有import

因此,我想我正在寻找一种方法,对所有python类(位于^{的子树中)进行某种组件扫描

怎样做更简单:查找装饰器还是查找子类?我该怎么做呢

提前谢谢


Tags: 模块pathpyimport装饰somerootfind
2条回答

好的,我想出了一个办法:

# /{some_proj_home_dir_1}/{some_path}/.../FindMe.py
class FindMe(object):
    pass



# /{some_proj_home_dir_1}/{some_path}/.../A.py

class A(object):

    ...

    def foo(self, project_home_dir):
        # going to mess with sys.path, so preserve current length of it, later to be restored
        sys_path_length = _append_dir_to_sys_path(project_home_dir)

        try:
            # dynamically load all modules in "the other" project
            self._load_all_modules(project_home_dir)
            # find all classes that needs to be found
            all_subclasses = self._find_all_needs_to_be_found()

            # do something with all_classes
            ...

        finally:
            # restore sys.path back to what it was
            sys.path = sys.path[:sys_path_length]


    def _load_all_modules(self, project_home_dir, subpackages=None):
        subdir = project_home_dir if subpackages is None else "/".join([project_home_dir] + subpackages)
        for (module_loader, name, ispkg) in pkgutil.iter_modules([subdir]):
            if ispkg:
                self._load_all_modules(project_home_dir, [name] if subpackages is None else subpackages + [name])
                return

            module_name = ".".join(subpackages)
            importlib.import_module("." + name, module_name)


    def _find_all_needs_to_be_found():
        return [FindMe] + FindMe.__subclasses__()


def _append_dir_to_sys_path(project_dir):
    path_length = len(sys.path)
    path = os.path.abspath(project_dir)

    while _is_part_of_package(path):
        sys.path.append(path)
        path = os.path.abspath(os.path.join(path, ".."))

    sys.path.append(path)

    return path_length


def _is_part_of_package(path):
    return os.path.isfile(os.path.join(path, "__init__.py"))



# /{some_different_proj_home_dir_2}/{some_path}/.../NeedsToBeFound.py
class NeedsToBeFound(FindMe):
    pass

现在调用A().foo({some_different_proj_home_dir_2})似乎有效:)

您是否考虑过将“希望在导入之前对其执行某些操作”的类/模块放入一个公共包并使用__init__.py?这就是这个文件的实际用途。;)

如果所有包都在一个模块中,您也可以将初始值设定项代码放入该模块中(在任何def之外)

另见例如

相关问题 更多 >