美文网首页
[python3] exec()函数

[python3] exec()函数

作者: StormZhu | 来源:发表于2018-05-20 14:28 被阅读0次

exec()介绍

exec(str [, globals [, locals]]函数执行一个表达式字符串并返回结果。参数globalslocals都是字典。exec的返回值固定为None。在python2python3中,这个函数的用法是不一样的,本文只考虑python3

例子

最简单的例子

program = 'a = 5\nb=10\nprint("Sum =", a+b)'
exec(program) # 省略 globals 和 locals参数
# Sum = 15

在这个例子中,将program转为python代码执行。

查看在exec中能够使用的变量和方法

exec('print(dir())')
# ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']

可以看到存在__builtins__,这也是exec调用的字符串中能够识别print()dir()函数的原因。

from math import *
exec('print(dir())')
# ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc']

看到在导入了math包后,exec的字符串参数就可以调用很多math库的数学函数,比如:

from math import * # 如果不导入库,下面的exec会报错
exec('print(sin(4))')
exec('print(exp(3))')
# -0.7568024953079282
# 20.085536923187668

还可以访问自己定义的函数和变量:

def func():
    print("in func")
x = 4
exec('print(dir())')
exec('print(x)')
exec('x=5\nprint(x)')
exec('func()')
# ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'func', 'x']
# 4
# 5
# in func

可以看到调用dir()时,多打印出了'func', 'x',表明在exec()中可以访问func函数和x变量。

限制exec中能够使用的变量和方法

大部分情况下,没必要让exec()能够使用太多方法和变量(会有安全隐患),所以可以通过globalslocals参数限制。globals里面存储全局参数,而locals里存储局部变量,当只传递globals时,既包括全局也包括局部。

只传递global,不传locals

from math import *
exec('print(dir())', {})

# ['__builtins__']

# This code will raise an exception
# exec('print(sqrt(9))', {})

可以看到在传递了空字典后,dir()的结果变少了很多,math包的内容也不能访问了。

也可以指定能够访问的函数:

from math import *
exec('print(dir())', {'sqrt': sqrt, 'pow': pow})

# object can have sqrt() module
exec('print(sqrt(9))', {'sqrt': sqrt, 'pow': pow})

传递的字典的key不一定要和函数名相同,可以自定义,比如:

from math import *
exec('print(dir())', {'squareRoot': sqrt, 'pow': pow})

# object can have squareRoot() module
exec('print(squareRoot(9))', {'squareRoot': sqrt, 'pow': pow})
# 下面的话会报异常
# exec('print(sqrt(9))', {'squareRoot': sqrt, 'pow': pow}) 

设定了'squareRoot': sqrt之后,在exec中就不能直接访问sqrt

还可以限制不能访问__builtins__

exec('print("111")', {'__builtins__': None})
# 会有异常,不能够访问print函数

同时传递globals和locals

普通用法:

from math import *
globalsParameter = {'__builtins__' : None}
localsParameter = {'print': print, 'dir': dir}
exec('print(dir())', globalsParameter, localsParameter)
# ['dir', 'print']



开始认为完全可以把参数传递进gloabls不就行了吗,测试如下:

# 和下面的写法相同
globalsParameter = {'__builtins__' : None,'print': print, 'dir': dir}
exec('print(dir())', globalsParameter)
# 结果和上面不一样
# ['__builtins__', 'dir', 'print'] 

测试结果发现和使用locals不一样,对于两者的区别没有找到详细解释,挖坑留待以后。

locals的优先级比gloabls高:

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()

# 60
# 33
# 34

可以看到gloablslocals中都定义了y,最终以local中为准。

神奇用法

看别人代码的时候,看到的比较神奇的用法,查阅资料也没有讲的详细的,但是理解一下也觉得是有道理的。简化之后:

python_source = '''print(dir())
def func(a, b):
  print(a+b)
print(dir())'''
global_namespace = {}
# 在执行完exec后,python_source字符串的func函数就存在于global_namespace字典中
exec(python_source, global_namespace)
func = global_namespace['func']# 通过字典查找函数
print(func) # 打印出函数的地址空间
func(4,6) # 调用函数

# ['__builtins__'] 最开始 由于传的是空字典,所以只能打印出'__builtins__'
# ['__builtins__', 'func'] 调用exec时,先产生了函数func,此时打印dir(),多了'func',并将这个函数放在了传递进去的 global_namespace 字典中
# <function func at 0x000001ED5C829048>
# 10

由上面可以看到,可以将获取exec调用的结果。

再自由发挥一下,如果字符串内的函数不传参,想使用外面的变量,结果上面整理的结果,可以写成:

a = 4
b = 5
python_source = '''def func():
  print(a+b)
  '''
global_namespace = {'a':a,'b':b}
# 在执行完exec后,python_source字符串的func函数就存在于global_namespace字典中
exec(python_source, global_namespace)
func = global_namespace['func']# 通过字典查找函数
print(func) # 打印出函数的地址空间
func() # 调用函数
a = 6 # 如果修改变量
func() # 调用函数 结果不变

# 9
# 9 外面的a变了,结果不变,说明传进去的a只是一个备份

可以通过gloabls参数将外面的变量传递进去,但是传递进去的只是备份,两边修改互不影响。

加入同时传入globals字典和locals字典时,变量和函数保留在哪个字典里面呢?

python_source = '''
def func():
  print('in func')
print(dir())
a = 5
print(dir())'''
global_namespace = {}
local_namespace = {}
exec(python_source, global_namespace,local_namespace)
a = local_namespace['a']# 通过字典查找变量
# func = global_namespace['func']# 报错
func = local_namespace['func']# 通过字典查找函数
# func = global_namespace['func']# 报错
print(a) # 打印出函数的地址空间
func()

# ['func']
# ['a', 'func']
# 5
# in func

可以看到,在有globals字典和locals字典是,内部变量都保存在locals字典中。

总结

  • 用户不传递globals字典时,默认会传递一个字典(是什么不知道,应该是当前的全局变量、函数和内置函数)。
  • 用户只传递globals字典时,如果传递空字典,exec内只能访问内置函数(__builtins__)
  • 用户只传递globals字典时,如果传递非空字典,exec内只能访问内置函数(__builtins__)、字典中包含的函数和变量(变量只是一个拷贝,内部修改了不会影响外面的变量,外面修改了不会影响内部的变量)。
  • 用户同时传递globals字典和locals字典时,使用方式同上,但是如果globals字典设置{'\_\_builtins\_\_' : None}locals字典设置其他函数和方法是,就不能访问内置函数(__builtins__)。注:这一点有点疑问!
  • exec执行的字符串中定义了函数和变量,是会保存在传递进去的globals字典中的,所以可以使用该字典获取字符串内的函数,并调用。
  • 同时传递globals字典和locals字典时,locals字典优先级高。

参考

  1. Python exec()
  2. python3-cookbook
  3. 菜鸟教程 Python3 exec 函数

相关文章

  • [python3] exec()函数

    exec()介绍 exec(str [, globals [, locals]]函数执行一个表达式字符串并返回结果...

  • php命令执行函数

    Exec函数 Exec函数的语法为: exec ( string $command [, array &$outp...

  • 代码审计——命令执行

    了解命令执行函数 system()passthru() 需回显示函数exec() shell_exec() ` `...

  • PHP的exec()

    php中可以使用 exec() 函数调用外部函数。 语法: string exec ( string $comma...

  • 进程控制二

    函数exec 函数介绍 这几个函数若出错返回-1,成功不返回。 这几个函数的区别 我们看到这几个函数都是exec开...

  • python3 range() 函数和 xrange() 函数

    python3 range 函数 python3 取消了 xrange() 函数,并且和 range() 函数合并...

  • Python可执行对象——exec、eval、compile

    Python提供的调用可执行对象的内建函数进行说明,涉及exec、eval、compile三个函数。exec语句用...

  • python3函数(一)

    python3中可以调用函数和定义函数。 1、调用函数 直接调用python3自带的函数 (1)函数abs(-10...

  • exec函数族

    fork()函数通过系统调用创建一个与原来进程(父进程)几乎完全相同的进程(子进程是父进程的副本,它将获得父进程数...

  • Head First C学习之systen()和exec()

    system()系统调用 system()会把字符串当成命令运行。 exec() 列表函数execl()、exec...

网友评论

      本文标题:[python3] exec()函数

      本文链接:https://www.haomeiwen.com/subject/xectjftx.html