动态编译

标准编程语言

对于 C 语言,代码一般要先编译,再执行。

  1. .c -> .exe

解释器语言

shell 脚本

  1. .sh -> interpreter

Byte Code 编译

Python, Java 等语言先将代码编译为 byte code(不是机器码),然后再处理:

  1. .py -> .pyc -> interpreter

eval 函数

  1. eval(statement, glob, local)

使用 eval 函数动态执行代码,返回执行的值:

In [1]:

  1. a = 1
  2.  
  3. eval("a+1")

Out[1]:

  1. 2

可以接收明明空间参数:

In [2]:

  1. local = dict(a=2)
  2. glob = {}
  3. eval("a+1", glob, local)

Out[2]:

  1. 3

这里 local 中的 a 先被找到。

exec 函数

  1. exec(statement, glob, local)

使用 exec 可以添加修改原有的变量。

In [3]:

  1. a = 1
  2.  
  3. exec("b = a+1")
  4.  
  5. print b
  1. 2

In [4]:

  1. local = dict(a=2)
  2. glob = {}
  3. exec("b = a+1", glob, local)
  4.  
  5. print local
  1. {'a': 2, 'b': 3}

执行之后,blocal 命名空间中。

警告

动态执行的时候要注意,不要执行不信任的用户输入,因为它们拥有 Python 的全部权限。

compile 函数生成 byte code

  1. compile(str, filename, mode)

In [5]:

  1. a = 1
  2. c = compile("a+2", "", 'eval')
  3.  
  4. eval(c)

Out[5]:

  1. 3

In [6]:

  1. a = 1
  2. c = compile("b=a+2", "", 'exec')
  3.  
  4. exec(c)
  5. b

Out[6]:

  1. 3

abstract syntax trees

In [7]:

  1. import ast

In [8]:

  1. tree = ast.parse("a+2", "", "eval")
  2.  
  3. ast.dump(tree)

Out[8]:

  1. "Expression(body=BinOp(left=Name(id='a', ctx=Load()), op=Add(), right=Num(n=2)))"

改变常数的值:

In [9]:

  1. tree.body.right.n = 3
  2.  
  3. ast.dump(tree)

Out[9]:

  1. "Expression(body=BinOp(left=Name(id='a', ctx=Load()), op=Add(), right=Num(n=3)))"

In [10]:

  1. a = 1
  2. c = compile(tree, '', 'eval')
  3.  
  4. eval(c)

Out[10]:

  1. 4

安全的使用方法 literal_eval ,只支持基本值的操作:

In [11]:

  1. ast.literal_eval("[10.0, 2, True, 'foo']")

Out[11]:

  1. [10.0, 2, True, 'foo']

原文: https://nbviewer.jupyter.org/github/lijin-THU/notes-python/blob/master/05-advanced-python/05.16-dynamic-code-execution.ipynb