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

Python:如何通过FrameInfo与标识符位置获取变量抽象表示并操作?

Python中基于FrameInfo的变量语义操作方案

核心问题结论

可以实现类似需求,但Python标准库未直接提供"带运行时语义的变量抽象表示",需结合inspect、ast、dis及解释器运行时状态来构建能力。


具体功能实现方案

1. 修改变量值

通过FrameInfo对应的frame对象,可直接操作局部、全局或闭包变量:

from inspect import stack

def modify_var(var_name, new_value):
    caller_frame = stack()[1].frame
    # 修改全局变量
    if var_name in caller_frame.f_globals:
        caller_frame.f_globals[var_name] = new_value
    # 修改局部变量(f_locals是副本,修改后需同步)
    elif var_name in caller_frame.f_locals:
        caller_frame.f_locals[var_name] = new_value
        caller_frame.f_locals.update(caller_frame.f_locals)
    # 修改闭包变量(cell对象)
    elif var_name in caller_frame.f_code.co_cellvars:
        cell = caller_frame.f_locals[var_name]
        cell.cell_contents = new_value

2. 查找定义与所有出现位置

  • 定义位置:解析调用者源代码的AST树,定位变量的定义节点(如Assign、FunctionDef),获取行号:
    from inspect import getsource, stack
    import ast
    
    def find_definition(var_name):
        caller_frame = stack()[1].frame
        source = getsource(caller_frame.f_code)
        tree = ast.parse(source)
        for node in ast.walk(tree):
            if isinstance(node, ast.Assign) and any(target.id == var_name for target in node.targets):
                return caller_frame.f_code.co_firstlineno + node.lineno - 1
            elif isinstance(node, (ast.FunctionDef, ast.Name)) and node.id == var_name:
                return caller_frame.f_code.co_firstlineno + node.lineno - 1
        return None
    
  • 所有出现位置:遍历AST树,收集所有引用该变量的节点位置,需注意作用域区分(全局/局部/闭包)。

3. 动态创建捕获变量的闭包

通过types模块操作函数的__closure__属性,手动绑定变量的cell对象:

from inspect import stack
import types

def create_var_closure(var_name):
    caller_frame = stack()[1].frame
    # 获取变量对应的cell对象
    if var_name in caller_frame.f_code.co_cellvars:
        cell = caller_frame.f_locals[var_name]
    else:
        # 普通局部变量转为cell
        cell = types.CellType()
        cell.cell_contents = caller_frame.f_locals.get(var_name, caller_frame.f_globals.get(var_name))
    
    def captured_closure():
        return cell.cell_contents
    
    captured_closure.__closure__ = (cell,)
    return captured_closure

示例代码问题修正

你的代码错误在于仅通过code_context[0]获取当前执行行的片段代码,无法处理列表推导式等嵌套场景。修正后的代码如下:

from inspect import stack, getsource
import io
from tokenize import tokenize, NAME, OP

def f():
    print('Doing something in function f.')
    detect()

def detect():
    caller_frame = stack()[1].frame
    caller_code = caller_frame.f_code
    # 获取调用者函数的完整源代码
    source = getsource(caller_code)
    source_lines = source.splitlines()
    # 计算当前行在函数内的索引
    line_idx = caller_frame.f_lineno - caller_code.co_firstlineno
    if 0 <= line_idx < len(source_lines):
        current_line = source_lines[line_idx]
        tokens = tokenize(io.BytesIO(current_line.encode()).read)
        target_name = caller_code.co_name
        while True:
            token = next(tokens, None)
            if not token:
                break
            if token.type == NAME and token.string == target_name:
                # 检查后续的()操作符
                open_paren = next(tokens, None)
                close_paren = next(tokens, None)
                if (open_paren and open_paren.type == OP and open_paren.string == '('
                    and close_paren and close_paren.type == OP and close_paren.string == ')'):
                    global_line = caller_code.co_firstlineno + line_idx
                    print(f'Current invocation of function {target_name} was in line {global_line} at position {token.start[1]}.')
                    break
        else:
            print(f'Invocation of {target_name} found in line {caller_frame.f_lineno} (nested context)')

f()
([f() for f in ()], f())

修正说明:

  • 使用getsource获取完整函数源代码,而非片段
  • 计算全局行号,避免嵌套场景的行号偏移问题
  • 严格匹配()的token类型(OP),提高鲁棒性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 04:07:05