Python:不运行函数检测其内部使用的外部/未定义变量
实现
detect_undefined函数:检测函数内未定义的外部变量 需要实现一个detect_undefined函数,能够分析目标函数的代码(无需运行函数),找出函数内部使用但未在函数自身范围内定义的变量,包括外部全局变量、模块引用等。以下是满足需求的实现方案:
解决方案代码
import ast import inspect import builtins def detect_undefined(func): # 获取目标函数的源码并解析为抽象语法树(AST) try: source = inspect.getsource(func) except (TypeError, OSError): return [] tree = ast.parse(source) # 定位目标函数的AST节点(处理带装饰器的情况) func_node = None for node in ast.walk(tree): if isinstance(node, ast.FunctionDef) and node.name == func.__name__: func_node = node break if not func_node: return [] # 收集函数内部已定义的所有名称:参数、内部赋值、导入的模块/对象 defined_names = set() # 添加函数参数(含可变参数) for arg in func_node.args.args: defined_names.add(arg.arg) if func_node.args.vararg: defined_names.add(func_node.args.vararg.arg) if func_node.args.kwarg: defined_names.add(func_node.args.kwarg.arg) # 遍历函数体,收集内部定义的名称 class DefinedNameCollector(ast.NodeVisitor): def visit_Assign(self, node): # 处理普通赋值(如a=3、a,b=1,2) for target in node.targets: if isinstance(target, ast.Name): defined_names.add(target.id) self.generic_visit(node) def visit_AugAssign(self, node): # 处理增量赋值(如a += 1) if isinstance(node.target, ast.Name): defined_names.add(node.target.id) self.generic_visit(node) def visit_Import(self, node): # 处理import语句(如import numpy、import numpy as np) for alias in node.names: defined_names.add(alias.asname or alias.name) self.generic_visit(node) def visit_ImportFrom(self, node): # 处理from...import语句(如from numpy import sum、from numpy import sum as np_sum) for alias in node.names: defined_names.add(alias.asname or alias.name) self.generic_visit(node) def visit_FunctionDef(self, node): # 忽略内部函数的定义(仅关注当前目标函数的变量) pass def visit_ClassDef(self, node): # 忽略内部类的定义 pass DefinedNameCollector().visit(func_node.body) # 收集函数内部所有被使用的名称 used_names = set() class UsedNameCollector(ast.NodeVisitor): def visit_Name(self, node): # 仅收集用于读取的变量(ast.Load表示读取操作) if isinstance(node.ctx, ast.Load): used_names.add(node.id) self.generic_visit(node) def visit_FunctionDef(self, node): # 忽略内部函数的使用 pass def visit_ClassDef(self, node): # 忽略内部类的使用 pass UsedNameCollector().visit(func_node.body) # 排除Python内置名称(如print、len等),得到最终未定义的变量列表 builtin_names = set(dir(builtins)) undefined = [name for name in used_names if name not in defined_names and name not in builtin_names] return sorted(undefined)
测试示例
示例1:引用外部模块
import numpy as np def add(a): return np.sum(a) print(detect_undefined(add)) # 输出: ['np']
示例2:内部导入模块
def add(a): import numpy as np return np.sum(a) print(detect_undefined(add)) # 输出: []
示例3:引用外部全局变量
b = 3 def add(a): return a + b print(detect_undefined(add)) # 输出: ['b']
示例4:使用函数参数(含默认值)
def add(a, b=3): return a + b print(detect_undefined(add)) # 输出: []
核心实现说明
- 源码解析:通过
inspect.getsource获取函数源码,再用ast.parse转换为AST,实现静态代码分析 - 定义名称收集:遍历AST节点,收集函数参数、内部赋值、导入语句中定义的所有名称
- 使用名称收集:遍历AST节点,收集所有以读取方式使用的变量名称
- 结果过滤:排除内置名称和已定义名称,得到未在函数内部定义的外部变量列表
内容的提问来源于stack exchange,提问作者sams-studio
相关产品推荐
相关产品推荐

