You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python如何在不导入子类所在模块的前提下获取类的所有子类

Python 无手动导入获取指定类所有子类实现

原生__subclasses__()方法只能识别已经被Python解释器加载的子类,未被导入的模块不会进入内存,自然无法被检索到。要实现无需手动写from xxx import xxx语句就能获取所有子类,核心逻辑是先自动扫描指定业务目录、动态加载所有相关模块,再递归收集所有子类即可。

实现代码

依赖Python标准库,无需安装第三方包:

import os
import sys
import importlib
from typing import List, Type

def get_all_subclasses(base_class: Type, scan_dir: str, package_prefix: str = "") -> List[Type]:
    """
    扫描指定目录下所有Python文件,动态导入模块后递归获取目标类的所有子类
    :param base_class: 待查找子类的父类对象
    :param scan_dir: 需要扫描的业务代码目录绝对路径
    :param package_prefix: 扫描目录对应的Python包导入前缀,例如扫描项目下的app目录则填"app"
    """
    # 遍历目录下所有py文件,跳过初始化文件、缓存文件
    for root, _, filenames in os.walk(scan_dir):
        for filename in filenames:
            if not filename.endswith(".py") or filename.startswith("__"):
                continue
            # 拼接模块的合法导入路径
            rel_path = os.path.relpath(os.path.join(root, filename), scan_dir)
            module_suffix = os.path.splitext(rel_path)[0].replace(os.sep, ".")
            full_module_path = f"{package_prefix}.{module_suffix}" if package_prefix else module_suffix
            # 跳过已导入模块,避免重复加载
            if full_module_path in sys.modules:
                continue
            importlib.import_module(full_module_path)
    
    # 递归收集所有层级的子类,覆盖多层继承场景
    subclass_list = []
    def _collect(current_cls: Type):
        for sub_cls in current_cls.__subclasses__():
            subclass_list.append(sub_cls)
            _collect(sub_cls)
    _collect(base_class)
    return subclass_list

使用示例

针对给出的类定义场景,假设类A、B、C都存放在项目的service目录下,对应包前缀为service,调用方式如下:

class A:
    name: str = ""

class B(A):
    name = "b_handler"

class C(A):
    name = "c_handler"

# 替换成你实际的项目目录路径和包前缀
subclasses = get_all_subclasses(
    base_class=A,
    scan_dir="/your/project/path/service",
    package_prefix="service"
)
# subclasses结果会自动包含B、C两个类,无需提前手动导入
print(subclasses)

匹配请求标识匹配的业务场景,可以直接遍历子类列表实现实例化:

def init_handler(handler_str: str):
    for cls in subclasses:
        if getattr(cls, "name", "") == handler_str:
            return cls()
    raise ValueError(f"不存在标识为{handler_str}的处理类")

注意事项

  • 扫描范围仅需要覆盖你自己写的业务代码目录,不要把虚拟环境、Python系统库目录加入扫描路径,否则会导入大量无关模块拖慢加载速度
  • 如果子类分散在多个不同的包路径下,可以多次调用扫描方法传入不同路径即可
  • 动态导入逻辑只会在工具初始化时执行一次,不会影响后续请求的处理性能

内容的提问来源于stack exchange,提问作者y.low

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 19:31:41