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

如何获取不含Docstring的Python函数源码?去除Docstring不影响哈希的方法

解决Python去除函数Docstring且不影响哈希的问题

针对你提到的两个核心需求——获取不含Docstring的函数源码,同时保证去除后不影响哈希(比如joblib.memory的缓存场景),以及inspect.getsource和inspect.getdoc的冲突问题,我整理了几个可靠的实践方案:

一、精准去除函数Docstring的推荐方法

1. 用ast模块解析语法树(最稳定)

ast模块能直接解析Python代码的抽象语法树,精准定位并移除Docstring节点,完全保留原代码的结构(除了Docstring本身)。这种方式处理后的源码格式稳定,哈希值不会轻易变动,完美适配joblib这类依赖哈希做缓存的场景。

示例代码:

import ast
import inspect

def remove_docstring(source_code):
    tree = ast.parse(source_code)
    # 遍历AST节点,移除函数/类的Docstring
    for node in ast.walk(tree):
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)) and ast.get_docstring(node):
            # 定位并移除Docstring对应的表达式节点
            if node.body and isinstance(node.body[0], ast.Expr) and isinstance(node.body[0].value, ast.Constant):
                node.body.pop(0)
    # 重新生成格式化后的源码
    return ast.unparse(tree)

# 用法示例
def my_func(a, b):
    """这是一个测试用的Docstring
    包含多行内容"""
    return a + b

# 获取原始源码后去除Docstring
raw_source = inspect.getsource(my_func)
cleaned_source = remove_docstring(raw_source)
print(cleaned_source)

这个方法的核心优势是:只移除Docstring,代码的缩进、变量名、逻辑结构完全不变,哈希值稳定性拉满。

2. 解决inspect的缓存冲突问题

你提到inspect.getsource和inspect.getdoc会互相影响——调用其中一个后,另一个返回的是“清理”后的结果。这是因为inspect内部会缓存解析结果,解决办法是直接从源码文件读取原始代码,绕过inspect的缓存:

def get_raw_source(func):
    file_path = inspect.getsourcefile(func)
    start_line = inspect.getsourcelines(func)[1]
    # 计算函数结束行号
    end_line = start_line + func.__code__.co_lnotab_size
    
    # 直接读取文件原始内容
    with open(file_path, 'r', encoding='utf-8') as f:
        lines = f.readlines()
    return ''.join(lines[start_line-1:end_line])

用这个方法拿到的是文件里的原始源码,不会被inspect的缓存干扰,之后再用上面的remove_docstring处理即可。

二、保证哈希稳定的关键注意事项

在joblib.memory这类场景中,哈希值稳定的核心是处理后的源码完全一致,所以要注意:

  • 避免用正则表达式直接处理(容易误删内容或改变缩进,比如匹配到字符串里的三重引号)
  • 使用ast.unparse(Python 3.9+)或astor库(兼容旧版本)生成处理后的源码,保证输出格式统一
  • 跨环境使用时,确保Python版本一致,不同版本的ast.unparse可能有细微格式差异

三、简单场景的正则补充方案(不推荐复杂场景)

如果你的函数Docstring都是标准格式(没有嵌套三重引号),可以用快速正则方案,但注意局限性:

import re

def remove_docstring_regex(source):
    # 匹配函数/类开头的三重引号Docstring
    pattern = r'(def |class |async def )(.*?:)\s*("""[\s\S]*?"""|\'\'\'[\s\S]*?\'\'\')'
    return re.sub(pattern, r'\1\2', source, flags=re.MULTILINE)

但遇到Docstring里包含三重引号(比如字符串示例),这个方法会失效,所以优先用ast方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 20:47:27