Python【4】:eval() & exec()


前言

本文主要介绍了 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 参数
  • 返回值
    • 返回表达式计算结果

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 解释器启动时创建,一直保留直到解释器退出
  • 顺序
    • 各命名空间创建顺序: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 语句,相比于 evalexec 可以执行更复杂的 Python 代码

以用来动态执行字符串代码,在 for 循环里面能快速执行大量类似于 list1= 1,list2=2,list3=3... 这样的语句,使代码显得更加简洁

exec(object[, globals[, locals]])
  • 参数含义
    • object:必选参数,表示需要被指定的 Python 代码。它必须是字符串或 code 对象。
      • 如果 object 是一个字符串,该字符串会先被解析为一组 Python 语句,然后再执行(除非发生语法错误)
      • 如果 object 是一个 code 对象,那么它只是被简单的执行
    • globals:可选参数,表示全局命名空间(存放全局变量),如果被提供,则必须是一个字典对象
    • locals:可选参数,表示当前局部命名空间(存放局部变量),如果被提供,可以是任何映射对象。如果该参数被忽略,那么它将会取与 globals 相同的值
  • 返回值
    • 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 值传递不起作用

猜你喜欢

转载自blog.csdn.net/HoraceYan/article/details/128697643