作为“动静”语言,Python在运行时加载一段代码并执行,必定是比须要编译的“动态语言”(比方C,Java)要不便多了。

执行形式

能够按是否返回后果,简略分为两种:exec和eval。

exec

exec负责执行字符串代码,可反对多行,可定义变量,但无奈返回后果

def pr(x):    print('My result: {}'.format(x))if __name__ == "__main__":    s = '''a = 15b = 3if a > b:    pr(a+b)'''    exec(s)

执行后果
> My result: 18

eval

eval能够返回后果,但只能执行单行表达式

def select_max(x, y):    return x if x > y else yif __name__ == "__main__":    a = 3    b = 5    c = eval('select_max(a , b)')    print("c is {}".format(c))

执行后果
> c is 5

运行时环境

从下面的代码示例能够看出,无论exec还是eval,它们的运行环境,就跟调用它们地位的代码一样:无论是全局的函数,还是部分的变量,只有在执行指定代码前定义过,就能够应用,并且exec中定义的变量,也能够被前面的代码援用。

如果有必要,咱们也能够在运行动静代码的时候,指定环境定义的内容,从而减少和屏蔽一些信息。exec与evel,都不止一个参数,他们的第二个和第三个参数,别离能够指定动静代码的globals与locals环境。

所谓globals,就是代码执行时的全局环境,能够通过globals()函数获取,返回后果是个dict,列出了所有全局变量和全局办法,包含用import的导入的模块和办法;同理,locals()函数能返回所有局部变量和部分办法。

而咱们调用动静代码的时候,如果像上面这样传参:

def select_max(x, y):    return x if x > y else yc = eval('select_max(3 , 5)', {}, {})

就会笼罩掉缺省的globals(第2个参数)和locals(第3个参数)设定,只能应用buildin的办法了,此时下面的代码就会报错——因为找不到select_max办法。为了让这个办法可用,咱们须要给其中某个dict赋值:

def select_max(x, y):    return x if x > y else yc = eval('select_max(3 , 5)', {'select_max':select_max}, {})

这看起来有点像脱了裤子放屁:明明间接用就好,为什么先笼罩掉,再赋一遍值呢?其实是出于安全性思考。

安全性

动静代码能力,通常是裸露给程序内部的,让配置人员能够扩大程序逻辑。然而如果不加限度,这个能力也是很危险的:比方通过调用open办法,能够关上任意文件,删除其内容。

所以比拟平安的形式,是把内置函数也禁用,只裸露容许内部调用的办法:

def select_max(x, y):    return x if x > y else yc = eval('select_max(3 , 5)', {"__builtins__": {}}, {'select_max':select_max})

留神:网上有很多文章,把__builtins__设置为None,经实测,至多在Python 3.7环境中是不可行的,该当设置为空字典

优化

编译

下面提到的两种形式示例,都是间接执行字符串。咱们必定能够想到,这些字符串再执行前,会先被python运行时“解析”(parsing)一遍,而解析的过程是很耗时的。所以,为了优化效率,能够先用compile函数,事后“编译”好,把字符串变成“代码”,每次执行的效率就会大大提高。

def select_max(x, y):    return x if x > y else yif __name__ == "__main__":    exp = compile('select_max(a , b)', '', 'eval')    for i in range(10):        a = i        b = i + 10        c = eval(exp)        print("c is {}".format(c))

能够看进去,字符串通过compile之后,变成了表达式,之后在循环中重复调用该表达式,会比每次解析字符串,效率高得多。同样,exec执行的内容,也能够先用compile编译为表达式。

留神:compile的第二个参数,是文件名(能够间接从文件读取代码),如果没有可间接置空

编译的环境

compile 和 eval/exec 能够不在同一个函数中被调用,那么它们领有的执行环境就不一样,但实际上compile并不查看环境,动静代码中用到的变量或办法,在编译时齐全能够不存在。比方把下面的代码改成上面的样子:

exp = compile('select_max(a , b)', '', 'eval')def select_max(x, y):    return x if x > y else yif __name__ == "__main__":    for i in range(10):        a = i        b = i + 10        c = eval(exp)        print("c is {}".format(c))

在定义select_max办法之前,就编译表达式,齐全不影响运行成果:

c is 10c is 11c is 12c is 13c is 14c is 15c is 16c is 17c is 18c is 19