未来的行为如何运作?

时间:2014-09-29 10:31:53

标签: python python-3.x python-2.x

这个转发端口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)版本的答案。

2 个答案:

答案 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上,这将捕获本地globslocs,允许它运行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])