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

Hol*_*ger 38 python exec python-2.7 python-3.x

下面的代码给出了不同的输出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")
Run Code Online (Sandbox Code Playgroud)

Python2 打印:

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

Python3 打印:

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

为什么Python2将函数b内部的变量绑定到execute函数字符串中的exec值,Python3而不执行此操作?我怎样才能实现Python2in 的行为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
Run Code Online (Sandbox Code Playgroud)

比较ids d和locals()shows表明它们是同一个对象.但在这些条件下b应该是一样的d['b'].我的例子有什么问题?

Mar*_*ers 42

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

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

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

def foo():
    a = 'spam'
    locals()['a'] = 'ham'
    print(a)              # prints 'spam'
Run Code Online (Sandbox Code Playgroud)

在Python 2中,使用该exec语句意味着编译器知道关闭本地范围优化(例如,切换LOAD_FAST到LOAD_NAME在本地和全局范围内查找变量).随着exec()作为一个功能,该选项不再可用,现在功能作用域总是优化.

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

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

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

该exec()文件是有关此限制很明确的:

注意:默认的locals的作用如下所述locals():不应尝试修改默认的locals字典.如果需要在函数返回后查看本地代码的效果,则传递显式的本地字典exec().


LRG*_*RGH 5

我会说这是python3的一个bug.

def u():
    exec("a=2")
    print(locals()['a'])
u()
Run Code Online (Sandbox Code Playgroud)

打印"2".

def u():
    exec("a=2")
    a=2
    print(a)
u()
Run Code Online (Sandbox Code Playgroud)

打印"2".

但

def u():
    exec("a=2")
    print(locals()['a'])
    a=2
u()
Run Code Online (Sandbox Code Playgroud)

失败了

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in u
KeyError: 'a'
Run Code Online (Sandbox Code Playgroud)

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

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()
Run Code Online (Sandbox Code Playgroud)

输出

{'l': {...}, 'a': 2}
{'l': {...}, 'a': 1}
Run Code Online (Sandbox Code Playgroud)

并且

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()
Run Code Online (Sandbox Code Playgroud)

输出

{'l': {...}, 'a': 2}
{'l': {...}, 'a': 2}
{'l': {...}, 'a': 2}
{'l': {...}}
Run Code Online (Sandbox Code Playgroud)

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

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