__future__ 导入与 eval、exec、compile 的交互机制探究
future 导入与 eval/exec/compile 的交互细节
咱们直接结合Python 2的实际案例来拆解这个问题,逻辑其实挺清晰的:
模块级的 future 导入会全局覆盖 eval/exec 的默认行为
当你在模块顶部导入__future__的特性时,这个设定会作用于整个模块的代码环境——包括通过eval()和exec()执行的代码片段。比如你给出的这段代码:
from __future__ import print_function print(1, end='!') eval(r"print(2, end='!')") exec r"print(3, end='!')"
运行后输出1!2!3!,这说明不管是模块内直接写的print,还是eval/exec里的print,都用了print_function带来的函数式调用方式,而不是Python 2默认的语句式print。
本质原因是__future__导入会修改当前模块的编译标志,而eval和exec默认会继承当前环境的编译标志,自然就跟着模块的设定走了。
exec 内部的 future 导入只对自身代码块生效
如果在exec的代码块里单独导入__future__特性,这个改动只会局限在这个代码块内部,不会影响外部的代码环境。举个例子:
# 外部还是Python 2默认的print语句 print 1 # 输出: 1 exec r""" from __future__ import print_function print(2, end='!') # 这里用的是函数式print """ # 外部依然用语句式print,完全不受exec内的导入影响 print 3 # 输出: 3
执行后的输出是:
1 2!3
这是因为exec在处理内部代码时,会为这段代码单独创建一个编译上下文,内部的__future__导入只会修改这个局部上下文的编译标志,不会波及外部模块的设定。
额外补充:compile() 可以手动控制编译标志
如果你需要更精细的控制,可以用compile()函数手动指定编译标志,甚至绕过模块级的__future__设定。比如:
from __future__ import print_function # 强制使用Python 2默认的print语句编译代码片段 code = compile("print 4", "<string>", "exec", dont_inherit=True) exec(code) # 输出: 4
这里的dont_inherit=True参数告诉编译器:不要继承当前模块的编译标志,所以这段代码即使在导入了print_function的模块里,依然会用旧的语句式print。
内容的提问来源于stack exchange,提问作者Aran-Fey
相关产品推荐
相关产品推荐

