忽略注释、格式、函数名时如何判断两个Python函数源码是否相等
解决方案
核心思路
CPython 没有公开的开关可以关闭字节码生成阶段的死代码删除优化,基于 __code__.co_code 的判断路径无法满足需求。
你需要的是源码层面的标准化对比,基于抽象语法树(AST)实现即可:AST 由源码直接解析生成,不会受字节码优化影响,天然过滤注释、无关空格、文档字符串,同时完整保留所有源码层面的语法节点,哪怕是不可达的死代码。
实现代码
import inspect import ast import hashlib from typing import Callable def get_func_standard_hash(func: Callable) -> str: # 获取函数的原始源码 source_code = inspect.getsource(func) # 将源码解析为 AST 节点 tree = ast.parse(source_code) func_def = tree.body[0] # 统一替换函数名,忽略函数名差异 func_def.name = "_placeholder_" # 移除函数的文档字符串 if ( len(func_def.body) > 0 and isinstance(func_def.body[0], ast.Expr) and isinstance(func_def.body[0].value, ast.Constant) ): func_def.body.pop(0) # 将 AST 转换为无格式差异的标准化字节串 normalized_content = ast.dump(func_def, indent=None, annotate_fields=False).encode("utf-8") # 计算并返回哈希值 return hashlib.sha256(normalized_content).hexdigest()
效果验证
用例1:逻辑等价但源码包含死代码
def f(a, b): return a return a def g(a, b): return a print(get_func_standard_hash(f) == get_func_standard_hash(g)) # 输出:False,符合预期
用例2:源码完全一致的两个不同函数
def f(a, b): return a def g(a, b): return a print(get_func_standard_hash(f) == get_func_standard_hash(g)) # 输出:True,符合预期
用例3:空格、注释、文档字符串不同的等价函数
def f( a , b): """Good job... hm, it was a good day...""" return a # NICE def g(a, b ): # HELLLOOOOOOO return a print(get_func_standard_hash(f) == get_func_standard_hash(g)) # 输出:True,符合预期
注意事项
- 该方案依赖函数源码可被读取,不适用于无对应源码的已编译
.pyc场景。 - 如果函数被装饰器包裹,可先用
inspect.unwrap(func)拿到原始函数后再计算哈希。
内容的提问来源于stack exchange,提问作者nbro
相关产品推荐
相关产品推荐

