• 《Python进阶系列》二十七:字符串类型代码的执行函数——eval()、exec()和compile()


    字符串类型代码的执行

    字符串类型代码的执行函数有三个,都是Python的内置函数

    • eval()执行字符串类型的代码,并返回最终结果。
    • exec()执行字符串类型的代码。
    • compile()将字符串类型的代码编码。代码对象能够通过exec语句来执行或者eval()进行求值。

    下面来一一介绍这三个函数。

    eval()

    执行一个字符串表达式,并返回表达式的值

    eval(expression[, globals[, locals]])
    
    • 1

    参数

    • expression:Python 表达式。
    • globals:必须是一个字典对象,否则程序会出错。当定义了globals参数之后eval函数的作用域会被限定在globals中。
    • locals:该参数掌控局部的命名空间,功能和globals类型,不过当参数冲突时,会执行locals处的参数。

    例子一:因为此处没有指定globalslocals,所以直接执行expression部分的内容。

    >>> a = 10
    >>> eval("a ** 3")
    1000
    
    • 1
    • 2
    • 3

    例子二:globals参数示例。

    >>> a = 10
    >>> g = {'a': 5}
    >>> eval("a + 1", g)
    6
    
    • 1
    • 2
    • 3
    • 4

    因为现在指定了globals,所以在expression部分的作用域就是globals指定的字典范围内。所以此时外面的a=10被屏蔽,取用字典中的值。
    例子三:locals参数示例

    >>> a = 10
    >>> b = 15
    >>> c = 20
    >>> g = {"a": 6, "b": 8}
    >>> t = {"b": 1000, "c": 10}
    >>> eval("a + b + c", g, t)
    1016
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    上面提到了,当有globalslocals时作用的范围域是在globalslocals中,所以a=1b=20c=30不会被应用。ac的值分别去字典g和字典t中的值,当globalslocals中都有参数b时取locals中的值。所以a=6b=100c=10

    exec()

    在Python中,exec()是一个十分有趣且实用的内置函数,不同于eval()函数只能执行计算数学表达式的结果的功能,exec()能够动态地执行复杂的Python代码,能够十分强大。参数如下:

    • object:必选参数,必须是字符串或 code 对象。如果 object 是一个字符串,该字符串会先被解析为一组 Python 语句,然后在执行(除非发生语法错误)。如果 object 是一个 code 对象,那么它只是被简单的执行。
    • globals:可选参数,表示全局命名空间(存放全局变量)必须是一个字典对象。
    • locals:可选参数,表示当前局部命名空间(存放局部变量)可以是任何映射对象。如果该参数被忽略,那么它将会取与 globals 相同的值。

    下面来看一些例子。
    例子一:执行简单的代码

    >>> a = 12
    >>> b = 30
    >>> exec("ans = a * b")
    >>> ans
    360
    
    • 1
    • 2
    • 3
    • 4
    • 5

    例子二func为字符串,它是一个递归地计算整数阶乘的函数。因为exec()仅支持stringcode object参数,所以我们要将该递归函数转化成一个字符串,当然,格式还是要Python代码的格式来,注意换行和缩进。刚才例子的输出结果为:

    >>> func = "def fact(n):\n\treturn 1 if n == 1 else n * fact(n - 1)
    >>> exec(func)
    >>> a = fact(5)
    >>> a
    120
    
    • 1
    • 2
    • 3
    • 4
    • 5

    例子三exec()的参数和上面的eval()一模一样。

    >>> x = 15
    >>> expr = "z = 30\nsum = x + y + z\nprint(sum)"
    >>> y = 20
    >>> exec(expr)
    65
    >>> exec(expr, {'x': 1, 'y': 2})
    33
    >>> exec(expr, {'x': 1, 'y': 2}, {'y': 100, 'z': 4})
    131
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    例子四:说到这里,可能有些疑问了?事实上,这些代码不是直接能够在Python中执行吗,为何还要多此一举?在实际项目中,我们有些时候会将Python代码写入一些文件中,举个例子,如以下的eg.txt,它储存了我们想要的Python代码,如下:

    def fact(n):
        if n == 1:
            return 1
        else:
            return n * fact(n - 1)
            
    t = fact(6)
    print(t)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    请再次注意,这是一个txt格式的Python代码。那么,我们如何调用它呢?答案就是exec()函数,代码如下:

    with open('E://eg.txt', 'r') as f:
        s = f.read()
     
    exec(s)
    
    • 1
    • 2
    • 3
    • 4

    参考:https://segmentfault.com/a/1190000014581721

    compile()

    compile()函数将一个字符串编译为字节代码或 AST 对象。代码对象可以被 exec()eval() 执行。以下是compile()方法的语法:

    compile(source, filename, mode[, flags[, dont_inherit]])
    
    • 1

    参数如下:

    • source :可以是常规的字符串、字节字符串,或者 AST 对象
    • filename:代码文件名称,如果不是从文件读取代码则传递一些可辨认的值。
    • mode:指定编译代码的种类。可以指定为 exec, eval, single
      • 如果是exec类型,表示这是一个序列语句,可以进行运行;
      • 如果是eval类型,表示这是一个单一的表达式语句,可以用来计算相应的值出来;
      • 如果是single类型,表示这是一个单一语句,采用交互模式执行,在这种情况下,如果是一个表达式,一般会输出结果,而不是打印为None输出。
    • flags:变量作用域,局部命名空间,如果被提供,可以是任何映射对象。
    • flagsdont_inherit是用来控制编译源码时的标志。

    编译代码时,如果语法出错会返回SyntaxError;如果代码包含一些空字节,则返回类型错误TypeError

    注意事项:当采用singleeval类型编译时,如果有多行代码,每行代码后面至少有一个换行符,否则在code模块编译时就会提示编译的源码不完整错误。在Python 3.2版本之后,允许输入Windows或Mac的换行符;当采用exec模式时,不需要在每个行后面输入换行符;在这个版本之后增加了优化参数。

    例子:

    >>> str = "for i in range(0,10): print(i)"
    >>> c = compile(str, '', 'exec')
    >>> exec(c)  # eval 也可以
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    >>> str = "3 * 4 + 5"
    >>> a = compile(str, '', 'eval')
    >>> eval(a)
    17
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

  • 相关阅读:
    vue:功能:table动态合并+前端导出
    【linux内核中的双向链表-02】list_for_each_safe
    【Pandas】数据分组groupby
    css3的作用及各类样式
    JS计算代码执行时间三大方法
    Python Opencv实践 - 入门使用Tesseract识别图片中的文字
    【面试题 - springcloud】- 网关
    一幅长文细学Vue(一)——项目开发工具
    系统设计 - 我们如何通俗的理解那些技术的运行原理 - 第四部分:微服务架构
    Python单元测试之道:从入门到精通的全面指南
  • 原文地址:https://blog.csdn.net/qq_37085158/article/details/126789688