首页Python【Python内置函数】e...

【Python内置函数】exec()函数

Python 提供了许多内置函数,这些函数是Python语言的一部分,可以直接在Python程序中使用而无需导入任何模块。

本系列将会陆续整理分享一些的Python内置函数。

文章配套代码获取有以下两种途径:
  • 通过百度网盘获取:
链接:https://pan.baidu.com/s/11x9_wCZ3yiYOe5nVcRk2CQ?pwd=mnsj 提取码:mnsj
  • 前往GitHub获取
https://github.com/returu/Python_built-in_functions





01
简介

exec() 函数用于执行存储在字符串或对象中的Python代码,并且支持控制变量作用域。
需要注意的是,exec()函数可以执行任意代码,因此如果执行的代码来自不可信的来源,可能会导致安全问题,例如代码注入攻击。

exec() 函数的基本语法如下:

exec(source, /, globals=Nonelocals=None, *, closure=None)
参数说明:
  • source:必需参数,可以是字符串或代码对象。如果是字符串,它会被当作Python代码执行;如果是代码对象,通常是通过compile()函数生成的;
  • globals:可选参数,指定一个字典,表示全局命名空间。如果省略或为None,则默认使用当前环境的全局命名空间。如果globals字典中没有包含键__builtins__,则会插入对内置模块builtins的引用。这允许执行的代码访问内置函数和变量。可以通过在globals中提供自定义的__builtins__字典,来控制执行代码可以访问哪些内置函数。
  • locals:可选参数,指定一个字典,表示局部命名空间。如果省略或为None,则默认使用globals参数指定的字典。如果globals也未指定,则使用当前环境的局部命名空间;
  • closurePyhon 3.11版本新增的参数。当代码对象包含自由变量(即不是函数局部变量但又不是全局变量的变量)时,closure参数用于用于传递闭包变量。closure必须是一个元组,与代码对象的co_freevars属性长度完全匹配。
返回值:

    exec() 函数的返回值始终是None,这是Python的明确设计。它的核心功能是动态执行代码片段(如赋值、循环、函数定义等),而不是返回计算结果。若需要获取动态代码的执行结果,需通过作用域变量传递。

    02
    使用

    下面是一些使用 exec() 函数的示例:

    • 示例 1:基本使用


    code = """
    def greet(name):
        return f'Hello, {name}!'

    print(greet('Alice'))
    "
    ""

    exec(code) # 输出:Hello, Alice!


    • 示例 2:全局和局部命名空间

    若不指定globalslocals,代码会在当前作用域执行,可能修改当前变量。


    x = 1
    exec("x = 42")
    print(x)  # 输出 42(直接修改了当前作用域的 x)

    通过传递自定义字典,限制代码的变量访问,隔离作用域。


    # 通过传递自定义字典,限制代码的变量访问,隔离作用域
    x = 1

    # 定义全局变量
    global_vars = {'x': 10, 'y': 10}

    # 定义局部变量
    local_vars = {'y': 20}

    # 执行代码
    exec("z = x + y", global_vars , local_vars)
    print(local_vars['z'])  # 输出 30
    print(x)  # 输出1(原始作用域的 x 不受影响)

    在实际使用中,建议通过限制 globals 和 locals 隔离代码,在沙箱环境中控制闭包变量的访问(例如限制敏感数据)。


    restricted_env = {
        '__builtins__': {},  # 禁用内置函数
        'safe_api': some_safe_function  # 仅暴露安全接口
    }
    exec(user_code, restricted_env)


    • 示例 3:限制内置函数

    默认情况下,exec 会自动将 __builtins__ 插入 globals 字典,提供对内置函数(如 open, eval)的访问,可通过自定义 globals 禁用内置函数:


    g = {'__builtins__': {}}  # 空字典禁用所有内置函数
    exec('print("test")', g)  # 报错:NameError(print 未定义)


    • 示例 4:使用代码对象

    如果传递的是代码对象,通常是通过 compile() 函数生成的。


    code = """
    x = 5
    y = 10
    print(f"
    x + y = {x + y}")
    "
    ""

    # 结合compile()预编译代码,模式应为'exec'或'single'
    compiled_code = compile(code, "<string>""exec")

    exec(compiled_code) # 输出:x + y = 15


    • 示例 5:使用 closure 参数

    closure 参数是Python 3.11新增的,用于支持闭包变量的显式传递。


    # 定义一个外部函数,生成闭包环境
    def outer(x):
        # inner 函数引用外部变量 x(形成闭包)
        def inner():
            return x  # 闭包变量 x
        return inner

    # 生成闭包函数并提取闭包信息
    closure_func = outer(42)

    # 获取 inner 函数的代码对象
    code_obj = closure_func.__code__
    # 获取闭包变量元组
    # closure_func.__closure__ 获取闭包中的 cell 对象(包含变量 x 的值)
    closure = closure_func.__closure__

    # 修改闭包变量的值
    closure[0].cell_contents = 100

    # 动态执行代码对象并传递闭包
    exec(code_obj,                         # 要执行的代码对象(inner 的函数体)
         {"__builtins__": __builtins__},   # globals全局变量
         {},                               # locals局部变量
         closure=closure                   # 通过关键字参数显式传递闭包变量
        )

    # 由于exec()不会返回函数对象,直接调用closure_func来验证闭包变量是否被修改
    print(closure_func())  # 输出: 100

    Python 3.11之前的版本中,无法直接通过exec()传递闭包变量。若需动态生成闭包函数,可以使用types.FunctionType手动绑定闭包。


    import types

    def outer(x):
        def inner():
            return x
        return inner

    # 生成闭包函数并提取闭包信息
    closure_func = outer(42)
    code_obj = closure_func.__code__
    closure = closure_func.__closure__

    # 修改闭包变量的值
    closure[0].cell_contents = 100

    # 手动创建函数对象并绑定闭包
    dynamic_inner = types.FunctionType(
        code_obj,
        globals={"__builtins__": __builtins__},  # 全局变量
        closure=closure                          # 直接绑定闭包
    )

    print(dynamic_inner())  # 输出: 100

    本篇文章来源于微信公众号: 码农设计师

    RELATED ARTICLES

    欢迎留下您的宝贵建议

    Please enter your comment!
    Please enter your name here

    - Advertisment -

    Most Popular

    Recent Comments