Python基础教程 使用 exec和eval 执行字符串及计算其结果

5.7.3 使用 exec 和 eval 执行字符串及计算其结果

有时候,你可能想动态地编写Python代码,并将其作为语句进行执行或作为表达式进行计算。这可能犹如黑暗魔法,一定要小心。 exec和eval现在都是函数,但exec以前是一种语句,而eval与它紧密相关。这就是我在这里讨论它们的原因所在。

警告本节介绍如何执行存储在字符串中的Python代码,这样做可能带来严重的安全隐患。如果将部分内容由用户提供的字符串作为代码执行,将无法控制代码的行为。在网络应用程序,如第15章将介绍的通用网关接口(CGI)脚本中,这样做尤其危险。

1.exec

函数exec将字符串作为代码执行。

>>> exec("print('Hello, world!')")

Hello, world!

然而,调用函数exec时只给它提供一个参数绝非好事。在大多数情况下,还应向它传递一个命名空间——用于放置变量的地方;否则代码将污染你的命名空间,即修改你的变量。例如,假设代码使用了名称sqrt,结果将如何呢?

>>> from math import sqrt

>>> exec("sqrt = 1")

>>> sqrt(4)

Traceback (most recent call last):

File "

", line 1, in ?

sqrt(4)

TypeError: object is not callable: 1

既然如此,为何要将字符串作为代码执行呢?函数exec主要用于动态地创建代码字符串。如果这种字符串来自其他地方(可能是用户),就几乎无法确定它将包含什么内容。因此为了安全起见,要提供一个字典以充当命名空间。

注意 命名空间(作用域)是个重要的概念,将在下一章深入讨论,但就目前而言,你可将命名空间视为放置变量的地方,类似于一个看不见的字典。因此,当你执行赋值语句x = 1时,将在当前命名空间存储键x和值1。当前命名空间通常是全局命名空间(到目前为止,我们使用的大都是全局命名空间),但并非必然如此。

为此,你添加第二个参数——字典,用作代码字符串的命名空间

>>> from math import sqrt

>>> scope = {}

>>> exec('sqrt = 1', scope)

>>> sqrt(4)

2.0

>>> scope['sqrt']

1

如你所见,可能带来破坏的代码并非覆盖函数sqrt。函数sqrt该怎样还怎样,而通过exec执行赋值语句创建的变量位于scope中。

请注意,如果你尝试将scope打印出来,将发现它包含很多内容,这是因为自动在其中添加了包含所有内置函数和值的字典__builtins__。

>>> len(scope)

2

>>> scope.keys()

['sqrt', '__builtins__']

2. eval

eval是一个类似于exec的内置函数。 exec执行一系列Python语句,而eval计算用字符串表示的Python表达式的值,并返回结果(exec什么都不返回,因为它本身是条语句)。例如,你可使用如下代码来创建一个Python计算器:

>>> eval(input("Enter an arithmetic expression: "))

Enter an arithmetic expression: 6 + 18 * 2

42

与exec一样,也可向eval提供一个命名空间,虽然表达式通常不会像语句那样给变量重新赋值。

警告 虽然表达式通常不会给变量重新赋值,但绝对能够这样做,如调用给全局变量重新赋值的函数。因此,将eval用于不可信任的代码并不比使用exec安全。当前,在Python中执行不可信任的代码时,没有安全的办法。一种替代解决方案是使用Jython(参见第17章)等Python实现,以使用Java沙箱等原生机制。

浅谈作用域

向exec或eval提供命名空间时,可在使用这个命名空间前在其中添加一些值。

>>> scope = {}

>>> scope['x'] = 2

>>> scope['y'] = 3

>>> eval('x * y', scope)

6

同样,同一个命名空间可用于多次调用exec或eval。

>>> scope = {}

>>> exec('x = 2', scope)

>>> eval('x * x', scope)

4

采用这种做法可编写出非常复杂的程序,但你也许不应这样做。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180925G0BNNF00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券