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

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))  # 输出: []

核心实现说明

  1. 源码解析:通过inspect.getsource获取函数源码,再用ast.parse转换为AST,实现静态代码分析
  2. 定义名称收集:遍历AST节点,收集函数参数、内部赋值、导入语句中定义的所有名称
  3. 使用名称收集:遍历AST节点,收集所有以读取方式使用的变量名称
  4. 结果过滤:排除内置名称和已定义名称,得到未在函数内部定义的外部变量列表

内容的提问来源于stack exchange,提问作者sams-studio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 04:12:19