Python【4】:eval() exec()
文章目录
- 前言
- 1. eval()
- 1.1. 语法
- 1.2. 命名空间与生命周期
- 1.2.1. 命名空间
- 1.2.2. 生命周期
- 1.3. 使用方法
- 2. exec()
- 2.1. 语法
- 2.2. 用法
- 2.2.1. 动态执行简单的字符串代码
- 2.2.2. 动态执行较复杂的代码
- 2.2.3. 执行文件中的 Python 代码
- 2.2.4. 传参
前言
本文主要介绍了 Python 中一对非常有意思的函数:eval()
和 exec()
这一对函数的作用大致相当,简单来说都可以执行字符串中的数学表达式且 exec()
函数的作用比 eval()
更大
1. eval()
1.1. 语法
eval()
函数用来执行一个字符串表达式,并返回表达式的值。即将字符串当成有效的表达式 来求值并返回计算结果。
eval(expression[, globals[, locals]])
- 参数含义
- expression – 表达式
- globals – 变量作用域,全局命名空间,如果被提供,则必须是一个字典对象
- locals – 变量作用域,局部命名空间,如果被提供,可以是任何映射对象
- 参数用法
- 当后两个参数都为空时,就是一个 string 类型的算术表达式,计算出结果即可
- 等价于
eval(expression)
- 等价于
- 当 locals 参数为空,globals 参数不为空时,先查找 globals 参数中是否存在变量,并计算
- 当两个参数都不为空时,先查找 locals 参数,再查找 globals 参数
- 当后两个参数都为空时,就是一个 string 类型的算术表达式,计算出结果即可
- 返回值
- 返回表达式计算结果
1.2. 命名空间与生命周期
1.2.1. 命名空间
- 定义
- 名称到对象的映射
- Python 通过命名空间来记录变量的轨迹
- 命名空间是一个 dictionary,键是变量名,值是变量值
- 各个命名空间是独立没有关系的,一个命名空间中不能有重名,但是不同的命名空间可以重名而没有任何影响
- 名称到对象的映射
- 分类
- Python 程序执行期间会有 2 个或 3 个活动的命名空间(函数调用时有3个,函数调用结束后2个)
- 按照变量定义的位置,可以划分为以下 3 类:
- Local:局部命名空间,每个函数所拥有的命名空间,记录了函数中定义的所有变量,包括函数的入参、内部定义的局部变量
- Global:全局命名空间,每个模块加载执行时创建的,记录了模块中定义的变量,包括模块中定义的函数、类、其他导入的模块、模块级的变量与常量
- Built-in:Python 自带的内建命名空间,任何模块均可以访问,放着内置的函数和异常
1.2.2. 生命周期
- 分类
- Local:局部命名空间,在函数被调用时才被创建,但函数返回结果或抛出异常时被删除
- 每一个递归函数都拥有自己的命名空间
- Global:全局命名空间,在模块被加载时创建,通常一直保留直到 Python 解释器退出
- Built-in:内建命名空间,在 Python 解释器启动时创建,一直保留直到解释器退出
- Local:局部命名空间,在函数被调用时才被创建,但函数返回结果或抛出异常时被删除
- 顺序
- 各命名空间创建顺序:Python 解释器启动 -> 创建内建命名空间 -> 加载模块 -> 创建全局命名空间 -> 函数被调用 -> 创建局部命名空间
- 各命名空间销毁顺序:函数调用结束 -> 销毁函数对应的局部命名空间 -> Python 虚拟机(解释器)退出 -> 销毁全局命名空间 -> 销毁内建命名空间
1.3. 使用方法
#1.eval无参实现字符串转化
s = '1+2+3*5-2'
print(eval(s)) #16
#2.字符串中有变量也可以
x = 1
print(eval('x+2')) #3
#3.字符串转字典
print(eval("{'name':'linux','age':18}"))
#输出结果:{'name':'linux','age':18}
#4.eval传递全局变量参数,注意字典里的:age中的age没有带引号,说明它是个变量,而不是字符串。
#这里两个参数都是全局的
print(eval("{'name':'linux','age':age}",{"age":1822}))
#输出结果:{'name': 'linux', 'age': 1822}
print(eval("{'name':'linux','age':age}",{"age":1822},{"age":1823}))
#输出结果:{'name': 'linux', 'age': 1823}
#eval传递本地变量,既有global和local时,变量值先从local中查找。
age=18
print(eval("{'name':'linux','age':age}",{"age":1822},locals()))
#输出结果:{'name': 'linux', 'age': 18}
print("-----------------")
print(eval("{'name':'linux','age':age}"))
2. exec()
2.1. 语法
exec
执行储存在字符串或文件中的 Python 语句,相比于 eval
,exec
可以执行更复杂的 Python 代码
以用来动态执行字符串代码,在 for
循环里面能快速执行大量类似于 list1= 1,list2=2,list3=3...
这样的语句,使代码显得更加简洁
exec(object[, globals[, locals]])
- 参数含义
- object:必选参数,表示需要被指定的 Python 代码。它必须是字符串或 code 对象。
- 如果 object 是一个字符串,该字符串会先被解析为一组 Python 语句,然后再执行(除非发生语法错误)
- 如果 object 是一个 code 对象,那么它只是被简单的执行
- globals:可选参数,表示全局命名空间(存放全局变量),如果被提供,则必须是一个字典对象
- locals:可选参数,表示当前局部命名空间(存放局部变量),如果被提供,可以是任何映射对象。如果该参数被忽略,那么它将会取与 globals 相同的值
- object:必选参数,表示需要被指定的 Python 代码。它必须是字符串或 code 对象。
- 返回值
exec
返回值永远为None
2.2. 用法
2.2.1. 动态执行简单的字符串代码
i = 12
j = 13
exec("answer=i*j")
print("Answer is %s"%answer)
这表明第三句的 exec()
函数能够正确地执行 Python 代码,并且能将计算结果赋予 answer 变量,就像真正的 Python 代码一样
2.2.2. 动态执行较复杂的代码
func = "def fact(n):\n\treturn 1 if n==1 else n*fact(n-1)"
exec(func)
a = fact(5)
print(a)
func 是字符串,它是一个递归地计算整数阶乘的函数。因为 exec()
仅支持 string 和 code object 参数,所以我们要将该递归函数转化成一个字符串,当然,格式还是要 Python 代码的格式来,注意换行和缩进
2.2.3. 执行文件中的 Python 代码
假设有 eg.txt 文件存储如下 Python 语句:
def fact(n):
if n==1:
return 1
else:
return n*fact(n-1)
t = fact(6)
print(t)
这是一个 txt 格式的 Python 代码,我们可以使用 exec()
语句运行它:
with open('eg.txt', 'r') as f:
s = f.read()
exec(s)
在上述代码中,我们先读取 eg.txt 文件的内容,再转交 exec()
函数执行
2.2.4. 传参
除了能执行 string 和 code object 外,还可以在 exec()
函数中加入参数,参数的传递可以写成字典 (dict) 形式
x = 10
expr = """
z = 30
sum = x + y + z
print(sum)
"""
def func():
y = 20
exec(expr)
exec(expr, {'x': 1, 'y': 2})
exec(expr, {'x': 1, 'y': 2}, {'y': 3, 'z': 4})
func()
语句分析:
- 在 expr 语句中,有三个变量
x
,
y
,
z
x, y, z
x,y,z, 其中
z
z
z 值已给定
- 我们可以在
exec()
函数外指定 x , y x, y x,y 的值 - 也可以在
exec()
函数中以字典的形式指定 x , y x, y x,y 的值
- 我们可以在
- 在最后的语句中,我们给出了
x
,
y
,
z
x, y, z
x,y,z 的值,并且
y
y
y 值重复,
exec()
函数接收后面一个 y y y 值,且 z z z 值传递不起作用