以下代码在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

为什么Python2会将execute函数中的变量b绑定到exec函数字符串中的值,而Python3不会这样做?我怎样才能达到Python3分之Python2的行为呢?我已经try 过将全局和局部的字典传递到Python3中的exec函数,但到目前为止没有任何效果.

--- EDIT ---

在阅读了Martijns的答案后,我用Python3进一步分析了这个问题.在下面的例子中,我给locals()的词义是dexec,但是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

dlocals()的ID进行比较表明,它们是同一个对象.但在这些条件下,b应该与d['b']相同.我的例子有什么不对?

推荐答案

Python2中的exec和Python3中的exec()之间有很大区别.您将exec视为一个函数,但在Python 2中它实际上是statement.

由于这种差异,在Python3中不能使用exec更改函数范围中的局部变量,即使在Python2中也可以.甚至连之前声明的变量都没有.

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

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

在Python 2中,使用exec语句意味着编译器知道关闭局部作用域优化(例如,从LOAD_FAST切换到LOAD_NAME,以查找局部和全局作用域中的变量).由于exec()是一个函数,该选项不再可用,函数范围现在优化了always.

此外,在Python 2中,exec语句使用PyFrame_LocalsToFastlocals()中找到的所有变量显式复制回函数locals,但前提是没有提供globalslocals参数.

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

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

exec() documentation强》非常明确地指出了这一限制:

Note:默认locals的作用如以下功能locals()所述:不应try 修改默认locals字典.如果需要在函数exec()返回后查看代码对局部变量的影响,请传递一个显式locals字典.

Python-3.x相关问答推荐

模型中的__str__方法在Django管理面板中生成大量重复查询

如何使用PySide6创建切换框架?

Pyvis和Networkx:如何根据源或目标使 node colored颜色 不同

如何将参数/值从测试方法传递给pytest的fixture函数?

Django 模型类方法使用错误的 `self`

Pandas 窗口聚合两个排序表

Pygame 错误地渲染等距图像

找到操作系统的图片文件夹的 CLI

列表中的重复数字与列表理解

Pandas 将列格式化为货币

例外:使用 Pyinstaller 时找不到 PyQt5 插件目录,尽管 PyQt5 甚至没有被使用

在气流中运行 DAG 时出现处理信号:ttou消息

内部如何使用 Python 语法?

为什么 Django South 1.0 使用 iteritems()?

Python 类型提示语法如何/为什么起作用?

将 args、kwargs 传递给 run_in_executor

Python - 类 __hash__ 方法和集合

如何为 anaconda python3 安装 gi 模块?

如何将python日志(log)级别名称转换为整数代码

如何从集合中删除多个元素?