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

Python 2和Python 3中exec函数的行为,python,python-2.7,python-3.x,exec,Python,Python 2.7,Python 3.x,Exec,以下代码在Python2和Python3中给出了不同的输出: 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.15

以下代码在
Python2
Python3
中给出了不同的输出:

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
3.2.3 (default, Apr 11 2012, 07:15:24) [MSC v.1500 32 bit (Intel)]
b: 1000000.0
42
Python3
打印:

2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)]
('b:', 1000000.0)
1000000.0
3.2.3 (default, Apr 11 2012, 07:15:24) [MSC v.1500 32 bit (Intel)]
b: 1000000.0
42
为什么
Python2
会将
execute
函数中的变量
b
绑定到
exec
函数字符串中的值,而
Python3
不会这样做?如何在
Python3
中实现
Python2
的行为?我已经尝试将全局和局部的字典传递给
Python3
中的
exec
函数,但到目前为止没有任何效果

---编辑---

在阅读了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()
的ID可以看出它们是同一个对象。但是在这些条件下,
b
应该与
d['b']
相同。我的示例中有什么错误?

我恐怕无法准确解释,但这基本上是因为函数中的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()函数中声明它。但是我发现这不起作用(这是我无法准确解释的部分)。

Python 2中的
exec
和Python 3中的
exec()
之间有很大区别。您将
exec
视为一个函数,但它实际上是Python 2中的一个语句

由于这一差异,您无法使用
exec
在Python3中更改函数范围中的局部变量,即使在Python2中也可以这样做。甚至不是以前声明的变量

locals()
仅在一个方向上反映局部变量。以下内容在2或3中都不起作用:

def foo():
    a = 'spam'
    locals()['a'] = 'ham'
    print(a)              # prints 'spam'
在Python 2中,使用
exec
语句意味着编译器知道关闭局部作用域优化(例如,从
LOAD\u FAST
切换到
LOAD\u NAME
,以查找局部和全局作用域中的变量)。由于
exec()
是一个函数,该选项不再可用,函数范围现在总是优化的

此外,在Python 2中,
exec
语句使用
PyFrame\u LocalsToFast
locals()
中找到的所有变量显式复制回函数locals,但前提是没有提供全局和局部参数

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

def execute(a, st):
    namespace = {}
    exec("b = {}\nprint('b:', b)".format(st), namespace)
    print(namespace['b'])
对于这一限制,政府非常明确:

注意:默认局部变量的作用与下面的函数
locals()
所述相同:不应尝试修改默认局部变量字典。如果需要在函数
exec()
返回后查看代码对局部变量的影响,请传递一个显式局部变量字典


我得说这是蟒蛇3的一只虫子

def u():
    exec("a=2")
    print(locals()['a'])
u()
打印“2”

打印“2”

但是

失败于

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

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

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': 1}
{'l': {...}, 'a': 2}
{'l': {...}, 'a': 2}
{'l': {...}, 'a': 2}
{'l': {...}}
显然,
exec
对局部变量的作用如下:

  • 如果在
    exec
    中设置了一个变量,并且该变量是一个局部变量,则
    exec
    会修改内部字典(由
    locals()
    返回的字典),而不会将其返回到原始状态。调用
    locals()。
    调用
    locals()
    来更新字典并不是python3的一个缺陷,因为它是有文档记录的,但并不直观。此外,对
    exec
    中的局部变量的修改不会改变函数的局部变量这一事实与python2有着明显的区别(文档中说“如果需要查看函数exec()返回后代码对局部变量的影响,请传递显式局部变量字典”),我更喜欢python2的行为
  • 如果在
    exec
    中设置了一个变量,并且该变量以前不存在,则
    exec
    会修改内部字典,除非该变量是在之后设置的。似乎
    locals()
    更新字典的方式中有一个bug;此错误通过在
    exec
    之后调用
    locals()
    来访问
    exec
    中设置的值
总之:

  • Python2和Python3中都没有bug
  • exec
    的不同行为源于
    exec
    在Python2中是一条语句,而在Python3中则是一个函数
请注意:

我没有告诉你任何新的事情。这只是真理的集合 在所有其他答案和评论中都可以找到。 我在这里所做的只是想揭示一些更模糊的细节

Python2和Python3之间唯一的区别是,
exec
确实能够在Python2中更改封闭函数的局部范围(因为它是一个语句,可以访问当前的局部范围),而在Python3中不能再这样做了(因为它现在是一个函数,所以在它自己的局部范围中运行)

然而,这种恼怒与
exec
语句无关,它只源于一个规范
exec "code"
exec("code", globals(), locals())