0

0

如何在函数作用域内安全获取所有局部变量构成的字典

心靈之曲

心靈之曲

发布时间:2026-03-02 10:37:00

|

230人浏览过

|

来源于php中文网

原创

如何在函数作用域内安全获取所有局部变量构成的字典

本文介绍一种不依赖运行时帧操作、避免 inspect.currentframe() 风险的可靠方法,通过动态提取并执行函数体代码,构建仅包含函数内部显式定义变量(不含参数、内置属性)的字典。

本文介绍一种**不依赖运行时帧操作、避免 `inspect.currentframe()` 风险**的可靠方法,通过动态提取并执行函数体代码,构建仅包含函数内部显式定义变量(不含参数、内置属性)的字典。

在 Python 开发中,有时需要在函数定义后“静态分析”其内部声明的变量(如用于调试、元编程或配置生成),但直接访问运行时局部作用域存在明显限制:inspect.currentframe().f_locals 仅在函数执行中有效,且返回结果受优化影响(如闭包、常量折叠)、不可靠;而 f_back.f_locals 更易引发 AttributeError 或捕获到错误作用域。

因此,更稳健的思路是:将函数体作为纯代码文本提取 → 剥离签名行 → 动态写入临时模块 → 导入执行 → 提取其 __dict__ 中的非双下划线变量。该方案规避了帧对象生命周期问题,确保结果可预测、可复现。

以下是一个生产就绪的实现(已优化安全性与健壮性):

暗壳AI
暗壳AI

Ark.art 包罗万象的艺术方舟,友好高效的设计助手

下载
import inspect
import os
import tempfile
import importlib.util
from types import FunctionType
from typing import Any, Dict
from textwrap import dedent

def get_function_defined_variables(func: FunctionType) -> Dict[str, Any]:
    """
    提取函数体内显式赋值的局部变量(不含参数、内置属性),返回 {name: value} 字典。

    注意:
    - 函数必须有源码(不能是交互式输入或编译字节码)
    - 参数名不会被包含(因参数在函数签名中定义,不在函数体赋值语句内)
    - 所有变量必须在函数体顶层直接赋值(不支持嵌套作用域如 if/for 内部定义)
    """
    try:
        source = inspect.getsource(func)
    except (OSError, IOError):
        raise ValueError(f"无法获取函数 {func.__name__} 的源码:需确保函数定义在 .py 文件中且未被混淆")

    # 分割函数头与函数体,跳过 def 行及可能的装饰器
    lines = source.strip().split('\n')
    body_lines = []
    in_body = False
    for line in lines:
        stripped = line.strip()
        if stripped.startswith('def ') or stripped.startswith('@'):
            continue  # 跳过装饰器和 def 行
        if not in_body and stripped and not stripped.startswith(' '):
            continue  # 跳过函数签名后的空行或注释前的首行
        if stripped and (stripped.startswith(' ') or stripped.startswith('\t')):
            in_body = True
            body_lines.append(line)
        elif in_body and not stripped:
            break  # 遇到空行结束函数体(保守策略)

    if not body_lines:
        return {}

    # 去除公共缩进,确保可执行
    body_clean = dedent('\n'.join(body_lines))

    # 创建临时文件(比硬编码 "TEMPORARYFILE.py" 更安全)
    with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False, encoding='utf-8') as f:
        f.write("from __future__ import annotations\n")  # 兼容未来语法
        f.write(body_clean)
        temp_path = f.name

    try:
        # 动态导入临时模块
        spec = importlib.util.spec_from_file_location("dynamic_module", temp_path)
        if spec is None:
            raise ImportError("无法为临时文件创建模块规范")
        module = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(module)  # 执行函数体(此时变量被定义在模块全局)

        # 过滤:仅保留用户定义的非 dunder 名称(排除 __name__, __file__ 等)
        return {
            k: v for k, v in module.__dict__.items()
            if not (k.startswith('__') and k.endswith('__'))
        }
    finally:
        # 清理临时文件
        try:
            os.unlink(temp_path)
        except OSError:
            pass  # 忽略清理失败(如已被删除)

# 使用示例
def example_func():
    var1 = 10
    var2 = 20
    var3 = 30
    var4 = '40'
    var5 = False
    # 注意:参数 `a`, `b` 不会出现在结果中
    a = 1  # 即使重赋值,也不算“定义”,但此处为演示——实际中应避免覆盖参数名

if __name__ == "__main__":
    result = get_function_defined_variables(example_func)
    print(result)
    # 输出:{'var1': 10, 'var2': 20, 'var3': 30, 'var4': '40', 'var5': False}

关键优势

  • ✅ 完全绕过 inspect.currentframe() 的 None 风险与作用域不确定性;
  • ✅ 支持类型提示(from __future__ import annotations);
  • ✅ 使用 tempfile.NamedTemporaryFile 避免文件名冲突与权限问题;
  • ✅ 显式过滤 __dunder__ 属性,结果纯净;
  • ✅ 抛出清晰异常,便于调试源码缺失等边界情况。

⚠️ 使用限制与注意事项

  • ❗ 函数必须具有可访问的源码(.py 文件中定义,非 exec() 动态生成);
  • ❗ 不支持函数体内条件分支(如 if True: x = 1)——因静态分析无法执行逻辑;
  • ❗ 若函数体含 import、class 或其他顶层语句,它们也会被导入并出现在结果中,请按需后处理;
  • ❗ 生产环境慎用于不受信函数(等效于 exec() 源码),建议配合沙箱或白名单校验。

该方法本质是将“函数体”视为一段独立可执行脚本,以模块级作用域模拟其局部行为,是目前兼顾可靠性、可读性与工程可用性的最佳实践。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1560

2023.10.24

if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

838

2023.08.22

class在c语言中的意思
class在c语言中的意思

在C语言中,"class" 是一个关键字,用于定义一个类。想了解更多class的相关内容,可以阅读本专题下面的文章。

747

2024.01.03

python中class的含义
python中class的含义

本专题整合了python中class的相关内容,阅读专题下面的文章了解更多详细内容。

23

2025.12.06

go语言闭包相关教程大全
go语言闭包相关教程大全

本专题整合了go语言闭包相关数据,阅读专题下面的文章了解更多相关内容。

150

2025.07.29

Golang 测试体系与代码质量保障:工程级可靠性建设
Golang 测试体系与代码质量保障:工程级可靠性建设

Go语言测试体系与代码质量保障聚焦于构建工程级可靠性系统。本专题深入解析Go的测试工具链(如go test)、单元测试、集成测试及端到端测试实践,结合代码覆盖率分析、静态代码扫描(如go vet)和动态分析工具,建立全链路质量监控机制。通过自动化测试框架、持续集成(CI)流水线配置及代码审查规范,实现测试用例管理、缺陷追踪与质量门禁控制,确保代码健壮性与可维护性,为高可靠性工程系统提供质量保障。

43

2026.02.28

Golang 工程化架构设计:可维护与可演进系统构建
Golang 工程化架构设计:可维护与可演进系统构建

Go语言工程化架构设计专注于构建高可维护性、可演进的企业级系统。本专题深入探讨Go项目的目录结构设计、模块划分、依赖管理等核心架构原则,涵盖微服务架构、领域驱动设计(DDD)在Go中的实践应用。通过实战案例解析接口抽象、错误处理、配置管理、日志监控等关键工程化技术,帮助开发者掌握构建稳定、可扩展Go应用的最佳实践方法。

37

2026.02.28

Golang 性能分析与运行时机制:构建高性能程序
Golang 性能分析与运行时机制:构建高性能程序

Go语言以其高效的并发模型和优异的性能表现广泛应用于高并发、高性能场景。其运行时机制包括 Goroutine 调度、内存管理、垃圾回收等方面,深入理解这些机制有助于编写更高效稳定的程序。本专题将系统讲解 Golang 的性能分析工具使用、常见性能瓶颈定位及优化策略,并结合实际案例剖析 Go 程序的运行时行为,帮助开发者掌握构建高性能应用的关键技能。

34

2026.02.28

Golang 并发编程模型与工程实践:从语言特性到系统性能
Golang 并发编程模型与工程实践:从语言特性到系统性能

本专题系统讲解 Golang 并发编程模型,从语言级特性出发,深入理解 goroutine、channel 与调度机制。结合工程实践,分析并发设计模式、性能瓶颈与资源控制策略,帮助将并发能力有效转化为稳定、可扩展的系统性能优势。

20

2026.02.27

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Django 教程
Django 教程

共28课时 | 4.6万人学习

Pandas 教程
Pandas 教程

共15课时 | 1.1万人学习

NumPy 教程
NumPy 教程

共44课时 | 3.5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号