如何获取不含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
相关产品推荐
相关产品推荐

