掌握Python eval()函数:解析动态代码执行的神奇之处

news2024/9/24 11:33:26

eval() 是 Python 提供的一个内置函数,用于动态执行字符串形式的表达式

虽然它功能强大,但同时也伴随着一定的风险和局限性。在本文中,我们将详细介绍 eval() 的用法、注意事项以及一些常见的应用场景。

一、eval() 函数的基本用法

![[内置函数eval()-20240620152417093.webp]]

基本语法:

eval(expression, globals=None, locals=None)
  • expression(必需):一个字符串,包含需要被解析并执行的有效 Python 表达式。
  • globals(可选):一个字典,指定全局命名空间,默认使用当前全局命名空间。
  • locals(可选):一个字典,指定局部命名空间,默认使用当前局部命名空间。

示例:

# 基本用法
result = eval('2 + 3')
print(result)  # 输出 5

# 使用全局和局部命名空间
x = 1
result = eval('x + 1', {'x': 10})
print(result)  # 输出 11

# 使用局部命名空间
result = eval('x + y', {'x': 1}, {'y': 2})
print(result)  # 输出 3

注意:你也可以使用 exec() 来动态执行 Python 代码。eval()exec() 的主要区别在于 eval() 只能执行或评估表达式,而 exec() 可以执行任何 Python 代码。

1.1 参数expression(必需)

eval() 的第一个参数称为 expression, 包含需要被解析并执行的有效 Python 表达式。

运行时,eval() 执行以下步骤:

  1. 解析 expression
  2. 将其编译为字节码
  3. 将其作为 Python 表达式进行评估
  4. 返回评估结果

如果将字符串传给eval()时,函数会返回执行字符串得到的值:

>>> eval("2 ** 8")
256
>>> eval("1024 + 1024")
2048
>>> eval("sum([8, 16, 32])")
56
>>> x = 100
>>> eval("x * 2")
200

默认情况下,eval() 可以访问全局名称,例如上面示例中的 x。

expression可以是字符串表达式或者代码对象,但不能是复合语句、赋值操作或者非完整的表达式。

expression 可接受字符串形式的表达式

对于 eval() 的第一个参数的命名——expression (表达式), 强调了该函数仅适用于表达式,而不适用于复合语句

Python 文档对表达式(expression) 和语句(Statement)的定义如下:

expression
A piece of syntax which can be evaluated to some value. In other words, an expression is an accumulation of expression elements like literals, names, attribute access, operators or function calls which all return a value. In contrast to many other languages, not all language constructs are expressions. There are also statements which cannot be used as expressions, such as while. Assignments are also statements, not expressions. (Source)

statement
A statement is part of a suite (a “block” of code). A statement is either an expression or one of several constructs with a keyword, such as if, while or for. (Source)

expression不接受复合语句

如果传入一个复合语句给 eval(),那么会出现 SyntaxError。看看以下的示例,其中尝试使用 eval() 执行一个 if 语句:

>>> x = 100
>>> eval("if x: print(x)")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    if x: print(x)
    ^
SyntaxError: invalid syntax
expression不接受赋值操作

eval() 也不允许赋值操作, 因为赋值操作是语句而不是表达式。

>>> eval("pi = 3.1416")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    pi = 3.1416
       ^
SyntaxError: invalid syntax
expression不接受不完整表达式

同样地,如果解析器无法理解输入的表达式,也会导致 SyntaxError。下面的例子就是一个解析器无法理解的“不完整”表达式。

>>> # Incomplete expression
>>> eval("5 + 7 *")
Traceback (most recent call last):
  File "<input>", line 1, in <module>
    eval("5 + 7 *")
  File "<string>", line 1
    5 + 7 *
          ^
SyntaxError: unexpected EOF while parsing
expression 可接受代码对象

可以使用 compile() 函数,将输入的字符串编译为代码对象或 AST 对象,再传入eval()执行

compile() 的基本语法:compile(source, filename, mode)
其中

  • source 要编译的源代码,可以是普通字符串、字节字符串或者 AST 对象。
  • filename 源代码所在的文件名。如果源代码来自字符串输入,通常设为 <string>
  • mode参数指定编译后的代码类型,常用的是 “exec”(用于执行代码)和 “eval”(用于评估表达式)。
>>> # Arithmetic operations
>>> code = compile("5 + 4", "<string>", "eval")
>>> eval(code)
9
>>> code = compile("(5 + 7) * 2", "<string>", "eval")
>>> eval(code)
24
>>> import math
>>> # Volume of a sphere
>>> code = compile("4 / 3 * math.pi * math.pow(25, 3)", "<string>", "eval")
>>> eval(code)
65449.84694978735

1.2 参数globals(可选)

eval() 的第二个参数称为 globals。它是可选的,是一个字典,用于为 eval() 提供全局命名空间。传递给 globals 的所有名称将在执行时供 eval() 使用。

全局名称是指在当前全局作用域或命名空间中可用的所有名称,可以在代码的任何地方访问它们。

默认状态

如果没有向 globals 参数传递自定义字典,那么该参数将默认为在调用 eval() 的环境中由 globals() 返回的字典:

>>> x = 100  # 一个全局变量
>>> y = 200  # 另一个全局变量
>>> eval("x + y")  # 访问两个全局变量
300

在上面的示例中, x 和 y包含在当前全局作用域中的全局变量。

自定义globals字典

>>> x = 100  # 一个全局变量
>>> eval("x + 100", {"x": x})
200
>>> y = 200  # 另一个全局变量
>>> eval("x + y", {"x": x})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'y' is not defined

如果向 globals 参数提供了一个自定义字典,那么 eval() 将只会使用该字典中的名称作为全局变量。任何在这个自定义字典之外定义的全局名称在 eval() 内部将无法访问。这就是为什么在上面的代码中尝试访问 y 时,Python 抛出 NameError 的原因:传递给 globals 的字典不包括 y。

你可以通过在字典中列出名称来将名称插入到 globals 中,这样这些名称在评估过程中将可用。例如,如果将 y 插入到 globals 中,那么上面示例中的 “x + y” 的评估将按预期工作

为了让 “x + y” 按预期工作, 可以把y加入自定义 globals 字典中,如下所示:

>>> eval("x + y", {"x": x, "y": y})
300

此外,还可以提供当前全局作用域中不存在的名称。 z在当前全局作用域中未定义, 为了实现它,需要为z提供一个具体的值。

>>> eval("x + y + z", {"x": x, "y": y, "z": 300})
600
>>> z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined

在这种情况下,eval() 能够访问 z,就像它是一个全局变量一样。

globals 背后的机制非常灵活。你可以将任何可见的变量(全局的、局部的或非局部的)传递给 globals。你还可以像上面的示例中那样传递自定义的键值对,例如 {“z”: 300}。eval() 将把它们都视为全局变量。

1.3 参数locals

locals 是另一个可选参数,用于保存一个字典。在这种情况下,这个字典包含 eval() 使用的局部变量名。

局部变量是指那些在给定函数内部定义的名称(变量、函数、类等)。局部变量只能从封闭的函数内部访问。当你编写函数时,可以定义这些类型的名称。

由于 eval() 已经被编写好,你无法向它的代码或局部作用域中添加局部名称。然而,你可以向 locals 参数传递一个字典,eval() 将把这些名称视为局部变量:

>>> eval("x + 100", {}, {"x": 100})
200
>>> eval("x + y", {}, {"x": 100})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'y' is not defined

第一个调用 eval() 中的第二个字典包含变量 x。这个变量被 eval() 解释为局部变量。换句话说,它被视为在 eval() 主体内定义的变量。

可以在表达式中使用 , 但是使用 y会返回NameError,因为 y 在全局命名空间或局部命名空间中都没有定义。

与 globals 类似,可以将任何可见的变量(全局的、局部的或非局部的)传递给 locals。也可以传递自定义的键值对,例如 {“x”: 100}。

注意:向locals 提供字典的前提是——需要向 globals 提供一个字典。

因为eval() 不接受关键字参数,如果使用关键字参数就会得到TypeError 错误,如下:

>>> eval("x + 100", locals={"x": 100})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: eval() takes no keyword arguments

因此,在向 locals 提供字典之前,你需要先向 globals 提供一个字典。

如果不向 locals 传递字典,则默认为传递给 globals 的字典。以下是一个示例,你向 globals 传递了一个空字典,并且没有向 locals 传递任何内容:

>>> x = 100
>>> eval("x + 100", {})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'x' is not defined

由于没有向 locals 提供自定义字典,参数默认为传递给 globals 的字典。在这种情况下,eval() 无法访问 x,因为 globals 是一个空字典。

globals 和 locals 的区别

在Python中,globals和locals之间的主要实际区别在于:

  • Globals(全局变量):无论是否提供自定义字典,Python都会自动向globals字典中插入一个 “builtins” 键。这确保了eval()可以访问所有Python内置的函数和对象。
  • Locals(局部变量):如果你向locals提供了自定义字典,在eval()执行期间这个字典将保持不变。在eval()内部,任何你在这个字典中定义的变量或函数都将作为局部名称使用,并且不会受到eval()外部的更改影响。
    简而言之,globals会被Python自动添加一个 “builtins” 键,而locals则保持你提供的静态字典不变。

*动态执行 VS 静态执行

下面两个result都能得到相同的结果(即 result 的值都是 4),但是它们有什么不同呢?

# 使用eval
result = eval("2 + 2")

# 不使用eval
result = 2 + 2

result = eval(“2 + 2”)

  • 动态执行eval 函数接受一个字符串参数,并将这个字符串当作 Python 表达式来执行。在执行时,字符串 "2 + 2" 被解析为一个数学表达式并计算其结果。
  • 灵活性:由于 eval 可以处理任意的字符串表达式,它提供了动态执行代码的能力。例如,可以从用户输入或文件中读取表达式并执行。
  • 风险eval 存在安全风险,因为它可以执行任意代码。如果传入的字符串包含恶意代码,可能会导致安全漏洞。

result = 2 + 2

  • 静态执行:这里的表达式是直接在代码中写明的,Python 在编译时就知道 2 + 2 是一个数学表达式,并会直接计算其结果。
  • 性能:静态表达式的执行效率通常比 eval 高,因为不需要解析字符串。
  • 安全性:这种方式不存在安全风险,因为所有的代码都是明确写在代码文件中的,不会执行任何外部或不受信任的代码。

参考链接: Python eval(): Evaluate Expressions Dynamically – Real Python

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2033737.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringIOC和SpringAOC

lombok插件 XML<!-- 加载资源文件 --><context:property-placeholder location"classpath:jdbc.properties"></context:property-placeholder><!-- 注入数据源 --><bean id"dataSource" class"com.mchange.v2.c3p0.ComboP…

Supervision 计算机视觉工具

简介 Supervision库是Python计算机视觉低代码工具&#xff0c;旨在为用户提供便捷高效的接口&#xff0c;以便处理数据集并直观地展示检测结果。绘制检测结果&#xff0c;统计指定区域内检测目标数量Supervision都提供了相应的接口 安装库 要求Python版本>3.8 1.安装无图像…

【机器学习】反向传播算法的直观解释、微积分原理以及反向传播中的链式法则

引言 正如有句话说的好&#xff0c;neurons-that-fire-together-wire-together&#xff08;一同激活的神经元关联在一起&#xff09; 文章目录 引言一、反向传播算法的直观解释1.1 前向传播1.2 计算误差1.3 反向传播误差1.4 更新权重 二、微积分原理2.1 损失函数 L L L2.2 链式…

javaEE WebServlet、SpringWebMVC、SpringBoot实现跨域访问的4种方式及优先级

文章目录 1. 前置知识2. 原理和解决方案总结2.1. 跨域不通过原理流程图2.2. 实现原理&#xff1a;添加以下http响应头2.3. 四种跨域实现方式及优先级&#xff08;从高到低&#xff09; 3. 具体实现代码3.1. 跨域全局配置方式-Filter(全适用)3.2. 跨域全局配置方式-SpringMvc3.3…

数字孪生模型制作教程虚拟现实城市模型制作3dmax数字城市glb/gltf

需要做数字孪生可以QQ可以联系这里&#xff0c;谢谢 下面开始教程 1打开3dmax软件&#xff0c;和需要做的建筑图片 2 在3dmax安图片先建一个长方体框架 3先给长方体贴一个墙体贴图 4在ps做贴图 5 做好贴图贴到3dmax中 6 然后ps再做下一张贴图 7 做好贴图贴到3dma…

[Redis] Redisson分布式锁原理及源码分析

目录 基于 Redis 的分布式锁 Redisson实现分布 Redisson分布式锁原理图 RedissonLock实现分布式锁源码分析 RedissonLock构造方法 lock()加锁 获取锁 锁续命逻辑 tryLockInnerAsync加锁lua脚本分析 unlock()解锁 基于 Redis 的分布式锁 实现方式: 使用 Redis 的 SE…

Idea2023.3版本创建spring Initializr没有JDK8

解决方法&#xff1a; https://start.aliyun.com

SOMEIP_ETS_037:echoUINT8RELIABLE_client_closes_TCP_connection_automatically

测试目的&#xff1a; 验证当所有服务停止时&#xff0c;DUT不会关闭TCP连接。 描述 本测试用例旨在检验DUT在停止所有服务时&#xff0c;是否能够保持TCP连接的活跃状态&#xff0c;而不发送FIN,ACK以关闭连接。 测试拓扑&#xff1a; 具体步骤&#xff1a; TESTER&#…

STM32学习笔记3 ---中断,定时器

目录 EXTI外部中断 NVIC嵌套中断向量控制器 EXTI外部中断 AFIO 旋转编码器 定时器TIM TIM定时中断 ​编辑​编辑 ​编辑 TIM输出比较&#xff08;OC&#xff09; 引脚重映射 舵机 直流电机 TIM输入捕获&#xff08;IC&#xff09; ​编辑 TIM编码器接口 附&#…

漏洞挖掘 | 某系统webpack接口泄露引发的一系列漏洞

信息搜集 这里找到从小穿一条裤子长大的兄弟&#xff0c;要挟他交出来他的统一账号&#xff0c;否则把小时候的照片挂网上&#xff0c;开始某大学的资产搜集&#xff0c;直接hunter搜索此大学域名 看有价值的站点&#xff0c;ok找到下面的站点 未授权敏感信息泄露越权任意用…

力扣高频SQL 50题(基础版)第四十二题之1517.查找拥有有效邮箱的用户

文章目录 力扣高频SQL 50题&#xff08;基础版&#xff09;第四十二题1517.查找拥有有效邮箱的用户题目说明实现过程准备数据实现方式结果截图总结 力扣高频SQL 50题&#xff08;基础版&#xff09;第四十二题 1517.查找拥有有效邮箱的用户 题目说明 表: Users -----------…

Dify on WeChat

Dify on WeChat 本项目为 chatgpt-on-wechat下游分支 额外对接了LLMOps平台 Dify&#xff0c;支持Dify智能助手模型&#xff0c;调用工具和知识库&#xff0c;支持Dify工作流。 Dify接入微信生态的详细教程请查看文章 手摸手教你把 Dify 接入微信生态 如果我的项目对您有帮助…

gin获得get和post请求参数,获得请求头信息

获得头信息 router.GET("/", func(c *gin.Context) {name : c.Query("id")fmt.Println(name)Token : c.GetHeader("Token")c.JSON(http.StatusOK, Token)})获得get和post信息 package mainimport ("fmt""github.com/SimonWang00…

Leetcode面试经典150题-236.二叉树的最低公共祖先

解法都在代码里&#xff0c;不懂就留言或者私信 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode(int x) { val x; }* }*/ class Solution {/**题目分析&#xff1a;本题是经典的二…

仓颉语言运行时轻量化实践

杨勇勇 华为语言虚拟机实验室架构师&#xff0c;目前负责仓颉语言静态后端的开发工作 仓颉语言运行时轻量化实践 仓颉Native后端&#xff08;CJNative&#xff09;是仓颉语言的高性能、轻量化实现。这里的“轻量化”意指仓颉程序运行过程中占用系统资源&#xff08;内存、CPU等…

数据分析:品牌营销如何借势小红书搜索流量

导语 近期&#xff0c;小红书推出《10大搜索趋势洞察》&#xff0c;在找答案这件事上&#xff0c;你永远可以相信小红书。 据悉&#xff0c;70%的小红狐月活用户使用搜索功能&#xff0c;用户平均每天搜索6次&#xff0c;三分之一的用户打开小红书的第一件事就是搜索&#xf…

haproxy知识点整理

haproxy知识点整理 haproxy七层代理负载均衡什么是负载均衡为什么使用负载均衡 负载均衡类型四层负载均衡七层负载均衡四层和七层的区别 环境搭建:客户端(client)haproxy服务器两台服务器hapserver1hapserver2 简单的haproxy负载均衡 haproxy的基本配置信息global配置proxies配…

2024高端网站设计公司推荐TOP3

随着互联网的飞速发展&#xff0c;现在的企业官网已经成为企业不可或缺的一部分&#xff0c;因为企业官网它不仅是企业品牌形象的延伸&#xff0c;也是连接客户、提升市场竞争力的重要工具。 以下简单阐述一下为何现代企业应当投资于高质量网站建设&#xff0c;搭建企业官网有…

html+css 实现图层水波纹效果

html+css 实现图层水波纹效果,废话不多说,直接上代码 <span class="quote-top"><i>水波纹</i><span class="ripple ripple-1"></span><span class="ripple ripple-2"></span><span class="…

打卡第四十一天:买卖股票的最佳时机

一、 买卖股票的最佳时机 题目 文章 视频 确定dp数组&#xff08;dp table&#xff09;以及下标的含义 dp[i][0] 表示第i天持有股票所得最多现金 。其实一开始现金是0&#xff0c;那么加入第i天买入股票现金就是 -prices[i]&#xff0c; 这是一个负数。dp[i][1] 表示第i天…