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())