Python组件扫描

Python组件扫描,python,python-decorators,Python,Python Decorators,我有一个叫“找到我”的装饰师。我想找到所有用它装饰的类 或者,我有一个名为FindMe的类,我想找到它的所有子类 为什么??因为我想在导入这些类之前对它们做一些处理 所以我读了关于__;子类__;和关于查找装饰器的内容。 我发现解决方案的问题是,必须在代码运行之前导入类 换句话说,如果我有: 在模块${proj_root}/some_path/FindMe.py中,一个类FindMeobject:, 在模块${proj_root}/some_other_path/NeedsToBeFound.p

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

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

为什么??因为我想在导入这些类之前对它们做一些处理

所以我读了关于__;子类__;和关于查找装饰器的内容。 我发现解决方案的问题是,必须在代码运行之前导入类

换句话说,如果我有:

在模块${proj_root}/some_path/FindMe.py中,一个类FindMeobject:, 在模块${proj_root}/some_other_path/NeedsToBeFound.py中,有一个类NeedsToBeFoundFindMe:, 和另一个模块${proj_root}/还有另一个路径/some_module.py, 如果某个_module.py看起来像: 然后,假设在过程中没有导入预期的类NeedsToBeFound,那么预期的类NeedsToBeFound就不会出现在结果中

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

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


提前谢谢

您是否考虑过在将要执行操作的类/模块导入公共包并使用_init__.py之前,将它们放入其中?这就是这个文件的实际用途

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

另见例如


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

# /{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}似乎是可行的:

Python没有内置的方法来实现您的要求。因此,您必须自定义编写一些东西来实现这一点。但我不推荐。您可以在python中获得一个类的所有子类。关于decorators,我不知道怎么做。如果你想在类被导入之前对它做些什么,你需要修改磁盘上的代码。该类在导入之前不存在。如果您希望在磁盘上查找某个模块的所有类,并生成一些代码来执行某些操作,也许可以看看像sphinx这样的文档生成器如何遍历模块并提取docstring…@Rashid'Lee'Ibrahim,是的,您可以在python中获得子类,但这些类需要在导入之前导入。。。您提供的链接是指同一模块中的不同类;如果这些类位于不同的包中,那么它将不起作用。它甚至明确地说:。。。如果子类的模块还没有导入-那么该子类还不存在,并且_子类__将找不到它。@CorleyBrigman,是的,该类在导入之前不存在。-这正是我所说的。。。这是一个有趣的线索,关于文档生成器。我会努力找到这样的源代码。谢谢不我是python新手,但据我所知,_init__;py.py在导入包时被隐式调用。。。在我的情况下,我想在加载之前做一些事情。。。
# /{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