Python 2和Python 3中exec函数的行为

时间:2013-02-26 09:57:18

标签: python python-2.7 python-3.x exec

以下代码在Python2Python3中提供了不同的输出:

from sys import version

print(version)

def execute(a, st):
    b = 42
    exec("b = {}\nprint('b:', b)".format(st))
    print(b)
a = 1.
execute(a, "1.E6*a")

Python2打印:

2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)]
('b:', 1000000.0)
1000000.0

Python3打印:

3.2.3 (default, Apr 11 2012, 07:15:24) [MSC v.1500 32 bit (Intel)]
b: 1000000.0
42

为什么Python2b函数中的变量execute绑定到exec函数的字符串中的值,而Python3不会做这个?如何在Python2中实现Python3的行为?我已经尝试将全局变量和本地变量的字典传递给exec中的Python3函数,但到目前为止没有任何工作。

---编辑---

在阅读Martijns回答后,我用Python3进一步分析了这一点。在下面的示例中,我将locals()字典d exec提供给d['b'],但b打印的内容不仅仅是打印from sys import version print(version) def execute(a, st): b = 42 d = locals() exec("b = {}\nprint('b:', b)".format(st), globals(), d) print(b) # This prints 42 print(d['b']) # This prints 1000000.0 print(id(d) == id(locals())) # This prints True a = 1. execute(a, "1.E6*a") 3.2.3 (default, Apr 11 2012, 07:15:24) [MSC v.1500 32 bit (Intel)] b: 1000000.0 42 1000000.0 True

d

locals()b的ID比较表明它们是同一个对象。但在这些情况下,d['b']应与{{1}}相同。我的例子有什么问题?

4 个答案:

答案 0 :(得分:39)

Python 2中的exec和Python 3中的exec()之间存在很大差异。您将exec视为一个函数,但它确实是一个语句在Python 2中。

由于存在这种差异,您无法使用exec在Python 3中更改函数范围中的局部变量,即使它在Python 2中是可能的。甚至不是先前声明的变量。

locals()仅反映一个方向的局部变量。以下从未在2或3中起作用:

def foo():
    a = 'spam'
    locals()['a'] = 'ham'
    print(a)              # prints 'spam'

在Python 2中,使用exec语句意味着编译器知道关闭本地范围优化(例如从LOAD_FAST切换到LOAD_NAME,以查找两者中的变量本地和全球范围)。当exec()是一个函数时,该选项不再可用,函数范围现在始终优化。

此外,在Python 2中,exec语句使用locals()PyFrame_LocalsToFast中找到的所有变量显式复制回函数本地,但前提是没有全局和 locals 参数。

正确的解决方法是为exec()电话使用新的命名空间(字典):

def execute(a, st):
    namespace = {}
    exec("b = {}\nprint('b:', b)".format(st), namespace)
    print(namespace['b'])

exec() documentation对此限制非常明确:

  

注意:默认 locals 的行为与下面的函数locals()相同:对默认 locals 字典的修改不应该是尝试。如果您需要在函数exec()返回后查看代码对本地的影响,请传递显式的 locals 字典。

答案 1 :(得分:5)

我说这是python3的错误。

def u():
    exec("a=2")
    print(locals()['a'])
u()

打印“2”。

def u():
    exec("a=2")
    a=2
    print(a)
u()

打印“2”。

但是

def u():
    exec("a=2")
    print(locals()['a'])
    a=2
u()

失败
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in u
KeyError: 'a'

---编辑--- 另一个有趣的行为:

def u():
    a=1
    l=locals()
    exec("a=2")
    print(l)
u()
def u():
    a=1
    l=locals()
    exec("a=2")
    locals()
    print(l)
u()

输出

{'l': {...}, 'a': 2}
{'l': {...}, 'a': 1}

def u():
    l=locals()
    exec("a=2")
    print(l)
    print(locals())
u()
def u():
    l=locals()
    exec("a=2")
    print(l)
    print(locals())
    a=1
u()

输出

{'l': {...}, 'a': 2}
{'l': {...}, 'a': 2}
{'l': {...}, 'a': 2}
{'l': {...}}

显然,exec对当地人的行动如下:

  • 如果在exec内设置变量并且此变量是局部变量,则exec修改内部字典(由locals()返回的字典)并且不会将其返回到原始状态。对locals()的调用更新了字典(如python文档的第2部分所述),忘记了exec中的值集。 调用locals()来更新字典的需要不是python3的错误,因为它已被记录,但它不直观。此外,exec内的本地修改不会改变函数的局部性这一事实是与python2的文档差异(文档说“如果您需要查看代码对本地代码的影响,请传递显式本地字典在函数exec()返回“)之后,我更喜欢python2的行为。
  • 如果变量在exec内设置且此变量之前不存在,则exec将修改内部字典,除非之后设置变量。似乎locals()更新字典的方式存在错误;通过exec之后调用locals(),此错误可以访问exec中的值集。

答案 2 :(得分:2)

总结一下:

  • Python 2和Python 3中都没有错误
  • exec的不同行为源于exec是Python 2中的一个语句,而它在Python 3中成为一个函数。
  

请注意:

     

我在这里没有说新的东西。这只是一个事实的集合   在那里找到所有其他答案和评论。   我在这里尝试的只是为一些更加模糊的细节带来光明。

Python 2和Python 3之间的唯一区别是,实际上,exec能够在Python 2中更改封闭函数的本地范围(因为它是一个语句并且可以访问当前的本地范围)并且不能再在Python 3中执行此操作(因为它现在是一个函数,因此在它自己的本地范围内运行)。

然而,这种烦恼与exec陈述无关,它只是源于一个特殊的行为细节:

locals()返回一些我想调用的东西&#34;一个范围明确的可变单例,在调用locals()之后,总是只引用本地范围内的所有变量&#34;

请注意locals()的行为在Python 2和3之间没有变化。因此,这种行为以及exec如何工作的变化看起来像是不稳定,但不是,因为它只是暴露了一些细节,总是在那里。

&#34;范围明确的可变单例引用局部范围内的变量&#34;意思?

  • 它是scope-wise singleton,因为无论您在同一范围内调用locals()的频率如何,返回的对象始终是相同的。
    • 因此观察id(d) == id(locals()),因为dlocals()指的是同一个对象,同一个单身,因为只能有一个(在不同的范围内你得到一个不同的对象,但在相同的范围内,你只能看到这一个。)
  • 它是mutable,因为它是普通对象,所以你可以改变它。
    • locals()强制对象中的所有条目再次引用本地范围中的变量。
    • 如果您更改了对象中的某些内容(通过d),则会更改该对象,因为它是一个普通的可变对象。
  • 单例的这些更改不会传播回本地作用域,因为对象中的所有条目都是references to the variables in the local scope。因此,如果您更改条目,这些更改单例对象,而不是更改引用之前引用指向的位置的内容&#34; (因此你不要改变局部变量)。

    • 在Python中,字符串和数字不可变。这意味着,如果您为某个条目指定了某些内容,则不会更改该条目所指向的对象,而是引入一个新对象并将该引用分配给该条目。例如:

      a = 1
      d = locals()
      d['a'] = 300
      # d['a']==300
      locals()
      # d['a']==1
      

    除了优化之外,还可以:

    • 创建新对象Number(1) - 这是其他一些单例,BTW。
    • 将指向此数字(1)的指针存储到LOCALS['a']中 (其中LOCALS应为内部本地范围)
    • 如果尚不存在,请创建SINGLETON对象
    • 更新SINGLETON,因此它会引用LOCALS
    • 中的所有条目
    • SINGLETON的指针存储到LOCALS['d']
    • 创建数字(300),单身,BTW。
    • 将指向这些Number(300)的指针存储到d['a']
    • 因此SINGLETON也会更新。
    • LOCALS 更新, 所以局部变量aLOCALS['a']仍然是数字(1)
    • 现在,再次调用locals()SINGLETON已更新。
    • 由于d引用SINGLETON,而非LOCALSd也会发生变化!
  

有关这个令人惊讶的细节的更多信息,为什么1是单身而300不是,请参阅https://stackoverflow.com/a/306353

     

但请不要忘记:数字是不可变的,因此如果您尝试将数字更改为其他值,则可以有效地创建另一个对象。

结论:

你无法将Python 2的exec行为恢复到Python 3(除非通过更改代码),因为无法再改变程序流之外的局部变量。

但是,您可以将Python 3的行为引入Python 2,这样您今天就可以编写运行相同的程序,无论它们是使用Python 3还是Python 2运行。这是因为在(较新的) Python 2你可以使用exec函数和参数(事实上,那些是2或3元组),允许使用相同的语法和Python 3中已知的相同语义:

exec "code"

(仅适用于Python 2)变为(适用于Python 2和3):

exec("code", globals(), locals())

但请注意,"code"不能再以这种方式改变本地封闭范围。另请参阅https://docs.python.org/2/reference/simple_stmts.html#exec

最后一句话:

Python 3中exec的更改很好。因为优化。

在Python 2中,您无法在exec之间进行优化,因为包含不可变内容的所有局部变量的状态可能会无法预测地发生变化。这不可能再发生了。现在,函数调用的通常规则同样适用于exec()所有其他函数。

答案 3 :(得分:1)

我担心我无法完全解释它,但它主要来自于函数内部的b是本地的,并且exec()似乎分配给全局b。您必须在exec语句中的函数中声明b是全局的。

试试这个:

from sys import version

print(version)

def execute1(a, st):
    b = 42
    exec("b = {}\nprint('b:', b)".format(st))
    print(b)

def execute2(a, st):
    global b
    b = 42
    exec("global b; b = {}\nprint('b:', b)".format(st))
    print(b)

a = 1.
execute1(a, "1.E6*a")
print()
execute2(a, "1.E6*a")
print()
b = 42
exec("b = {}\nprint('b:', b)".format('1.E6*a'))
print(b)

哪个给了我

3.3.0 (default, Oct  5 2012, 11:34:49) 
[GCC 4.4.5]
b: 1000000.0
42

b: 1000000.0
1000000.0

b: 1000000.0
1000000.0

您可以在功能外部看到全局b被自动拾取。在函数内部,您将打印本地b。

请注意,我认为exec()始终首先使用全局b,因此在execute2()中,您不需要在exec()函数中声明它。但我觉得这不起作用(这是我无法解释的部分)。