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

如何从Python代码自动生成Function Hierarchy Diagrams?

生成Python项目函数层次结构图的可行方案

针对你的需求,这里有几种实用的实现方式,涵盖静态分析和运行时跟踪两种场景:

1. 自定义脚本:结合内置模块 + 绘图库

如果需要完全自定义层次结构的展示逻辑,推荐用Python内置模块做静态分析,再配合绘图库生成可视化图:

方案A:用inspect模块快速实现

inspect可以直接获取模块、类、函数的定义信息,结合pydot(依赖graphviz)生成图:

import inspect
import pydot

def build_func_hierarchy(module, graph=None):
    if graph is None:
        graph = pydot.Dot(graph_type='digraph', rankdir='TB')
    
    # 遍历模块内的类和函数
    for name, obj in inspect.getmembers(module):
        # 处理普通函数
        if inspect.isfunction(obj) and obj.__module__ == module.__name__:
            func_node = pydot.Node(f"Func: {name}", shape='box')
            graph.add_node(func_node)
            # 简单提取函数内的本地调用(可优化为ast解析)
            source_lines = inspect.getsource(obj).split('\n')
            for line in source_lines:
                stripped = line.strip()
                if '(' in stripped and ')' in stripped and not stripped.startswith('def'):
                    call_name = stripped.split('(')[0].split('.')[-1]
                    # 检查是否为当前模块内的函数
                    if call_name in [m[0] for m in inspect.getmembers(module) if inspect.isfunction(m[1])]:
                        graph.add_edge(pydot.Edge(func_node, f"Func: {call_name}"))
        
        # 处理类及类方法
        elif inspect.isclass(obj) and obj.__module__ == module.__name__:
            class_node = pydot.Node(f"Class: {name}", shape='oval')
            graph.add_node(class_node)
            for method_name, method in inspect.getmembers(obj, inspect.isfunction):
                if method.__name__ != '__init__' or method.__code__.co_code != b'':
                    method_node = pydot.Node(f"{name}.{method_name}", shape='box')
                    graph.add_node(method_node)
                    graph.add_edge(pydot.Edge(class_node, method_node))
    
    return graph

# 使用示例
import your_project_module
graph = build_func_hierarchy(your_project_module)
graph.write_png('func_hierarchy.png')

注意:这个版本的调用检测比较基础,若要精准识别跨模块调用、排除内置函数等,建议换成ast模块解析源码。

方案B:用ast模块做精准静态分析

ast可以直接解析Python代码的语法树,能更准确提取函数定义、类方法和调用关系:

import ast
import pydot

class HierarchyVisitor(ast.NodeVisitor):
    def __init__(self):
        self.graph = pydot.Dot(graph_type='digraph', rankdir='TB')
        self.current_scope = None

    def visit_ClassDef(self, node):
        class_node = pydot.Node(f"Class: {node.name}", shape='oval')
        self.graph.add_node(class_node)
        # 记录当前类作为方法的父节点
        prev_scope = self.current_scope
        self.current_scope = class_node.get_name()
        # 遍历类内方法
        for item in node.body:
            if isinstance(item, ast.FunctionDef):
                method_node = pydot.Node(f"{node.name}.{item.name}", shape='box')
                self.graph.add_node(method_node)
                self.graph.add_edge(pydot.Edge(class_node, method_node))
        self.generic_visit(node)
        self.current_scope = prev_scope

    def visit_FunctionDef(self, node):
        func_node = pydot.Node(f"Func: {node.name}", shape='box')
        self.graph.add_node(func_node)
        # 如果在类内,已经处理过关联;如果在模块内,直接添加
        if self.current_scope and not self.current_scope.startswith("Class:"):
            self.graph.add_edge(pydot.Edge(self.current_scope, func_node.get_name()))
        # 记录当前函数作为内部调用的父节点
        prev_scope = self.current_scope
        self.current_scope = func_node.get_name()
        self.generic_visit(node)
        self.current_scope = prev_scope

# 使用示例
with open('your_module.py', 'r', encoding='utf-8') as f:
    code_tree = ast.parse(f.read())
visitor = HierarchyVisitor()
visitor.visit(code_tree)
visitor.graph.write_svg('func_hierarchy.svg')

2. 用pycallgraph生成运行时函数调用层次

如果需要展示项目运行时的实际函数调用路径,可以用pycallgraph:

  • 先安装依赖:pip install pycallgraph graphviz
  • 运行你的项目并生成图:
    pycallgraph graphviz -- ./your_entry_script.py
    

运行后会生成一个pycallgraph.png文件,清晰展示函数调用的层次和调用次数,适合排查性能瓶颈或梳理执行流程。

3. Sphinx文档工具生成结构化层次

如果你的项目已经在用Sphinx做文档,可以借助autodoc和graphviz扩展生成结构化的层次图:

  1. 配置Sphinx的conf.py,启用扩展:
    extensions = ['sphinx.ext.autodoc', 'sphinx.ext.graphviz']
    
  2. 在rst文档中用automodule提取模块结构,或者手动编写dot代码生成层次图:
    .. graphviz::
       :align: center
    
       digraph {
           rankdir=TB;
           "Module: core" -> "Func: init_app";
           "Module: core" -> "Class: User";
           "Class: User" -> "Method: get_profile";
       }
    

构建文档时会自动生成对应的可视化层次图,适合整合到项目文档中。

内容的提问来源于stack exchange,提问作者Ognjan Ognjanovic

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 16:40:05