[python3] exec()函数

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 函数
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容

  • 内置函数Python解释器内置了许多功能和类型,总是可用的。他们是按字母顺序列在这里。 abs(x)返回一个数的绝...
    uangianlap阅读 1,234评论 0 0
  • 大多数编程语言都有自己的内置函数,Python也不例外,同样提供了丰富的内置函数,其中包括算术函数、字符串操作函数...
    简书冷雨阅读 907评论 0 0
  • 内置函数续: 35.bytes 函数 bytes 函数返回一个新的 bytes 对象,该对象是一个 0 <= x ...
    一只小釆鸟阅读 446评论 0 1
  • 1. abs(x) abs()函数返回数字(可为普通型、长整型或浮点型)的绝对值。如果给出复数,返回值就是该复数的...
    chen_000阅读 402评论 0 0
  • 晚上又部门聚餐,一群人吃吃喝喝嚷嚷叫叫闹闹感官上好不热闹,但心是有一切深透感知力的,人与人之间的亲密与否实在难以逃...
    光呀么光呀么光哥哥阅读 286评论 0 0