这个转发端口exec
的代码如何工作?
# Implementation of exec_ is from ``six``:
if PY3:
import builtins
exec_ = getattr(builtins, "exec")
else:
def exec_(code, globs=None, locs=None):
"""Execute code in a namespace."""
if globs is None:
frame = sys._getframe(1)
globs = frame.f_globals
if locs is None:
locs = frame.f_locals
del frame
elif locs is None:
locs = globs
exec("""exec code in globs, locs""")
该片段是从Python Future复制的,因为我太懒了,无法找到Martijn Pieters所链接的the original from Six。
欢迎特定于Six(同一条reraise
)版本的答案。
答案 0 :(得分:4)
对于Python 3:
if PY3:
这是相对简单的:
import builtins
exec_ = getattr(builtins, "exec")
使用getattr
的原因是,在Python 2上exec
是一个声明,您要避免:
>>> builtins.exec
File "<stdin>", line 1
builtins.exec
^
SyntaxError: invalid syntax
使用字符串进行查找可以解决exec
不是有效标识符的问题。
如果是Python 2:
else:
我们希望在Python 3上将exec_
定义为exec
,这意味着它如下所示:
def exec_(code, globs=None, locs=None):
"""Execute code in a namespace."""
让我们快速查看文档:
help(exec)
#>>> Help on built-in function exec in module builtins:
#>>>
#>>> exec(...)
#>>> exec(object[, globals[, locals]])
#>>>
#>>> Read and execute code from an object, which can be a string or a code
#>>> object.
#>>> The globals and locals are dictionaries, defaulting to the current
#>>> globals and locals. If only globals is given, locals defaults to it.
#>>>
这应该有助于解释下一部分:
if globs is None:
frame = sys._getframe(1)
globs = frame.f_globals
如果globs is None
,我们希望它默认为调用者范围的全局。这实际上非常复杂。
首先我们获取外框:
import sys
help(sys._getframe)
#>>> Help on built-in function _getframe in module sys:
#>>>
#>>> _getframe(...)
#>>> _getframe([depth]) -> frameobject
#>>>
#>>> Return a frame object from the call stack. If optional integer depth is
#>>> given, return the frame object that many calls below the top of the stack.
#>>> If that is deeper than the call stack, ValueError is raised. The default
#>>> for depth is zero, returning the frame at the top of the call stack.
#>>>
#>>> This function should be used for internal and specialized
#>>> purposes only.
#>>>
那么 sys._getframe
只是调用者的范围。框架是函数执行发生的环境,它存储了几个有趣的属性,例如f_globals
- 从该函数的角度来看全局变量。
然后如果:
if locs is None:
locs = frame.f_locals
默认locs
到当地人。
删除该框架以防止在exec
调用期间不必要地保持活动。
del frame
Penultimately,
elif locs is None:
locs = globs
这是为了完成“如果只给出全局变量,本地人默认为它。”一部分。
然后是最有趣的部分:
exec("""exec code in globs, locs""")
仅运行exec code in globs, locs
意味着在Python 3上编译时,这会出错,因为exec
不再是语句。因此,进行了外部exec("""exec code in globs, locs""")
调用。
在Python 3上,这将永远不会运行,但编译。在Python 2上,这将捕获本地globs
和locs
,允许它运行exec code in globs, locs
。
Please do look at Antti Haapala's answer, though因为他们指出的这些shennanigans实际上与其有些无关,包括Martijn Pieters提出的一些信息。
答案 1 :(得分:1)
最终exec("""exec code in globs, locs""")
确实是多余的,通过将代码编写为
SyntaxError
的Python 3中进行解析
exec (code) in globs, locs
将在python 2中将其解析为
exec code in globs, locs
在Python 3中,它实际上与
相同tuple([exec(code) in globs, locs])
但是,Python中的exec
语句始终能够接受元组参数,因此exec
行可以写为
exec(code, globs, locs)
哪个在Python 2和3中运行相同,甚至
exec(tuple([code, globs, locs]))
(仅适用于Python 2);事实上,Python 3中唯一可以做到的事情是Python 2中不可能以及为什么需要这个包装器才能使它成为一个真正的函数/方法对象,可以将其分配给另一个名称或作为参数传递,如目前
exec_ = exec
Python中的是SyntaxError
。
同样在Python 3中,
print argument, argument, argument,
是语法错误,但在多语言代码中,可以始终包含
if PY2:
print >> sys.stdout, argument, argument, argument,
将在Python 3中解析为
tuple([print >> sys.stdout, argument, argument, argument])