8

exec在一些 Python 2 代码中使用该语句,并且我试图使该代码与 Python 2 和 Python 3 兼容,但在 Python 3 中,exec已从语句更改为函数。是否可以编写与 Python 2 和 3 兼容的代码?我读过Python 2 和 Python 3 dual development,但我对exec语句/函数更改的具体解决方案感兴趣。

我意识到这exec通常是不鼓励的,但我正在构建一个 Eclipse 插件,它在 PyDev 之上实现实时编码。有关更多详细信息,请参阅项目页面

4

3 回答 3

13

一些Python 移植指南exec 出错了

如果您需要传入全局或本地字典,则需要定义具有两种不同实现的自定义函数,一种用于 Python 2,另一种用于 Python 3。通常six包括一个出色的实现,称为exec_().

无需此类自定义函数即可将 Python 2 代码移植到 Python 3 (*)。您可以在 Python 2中执行exec(code), exec(code, globs)and ,并且它可以工作。exec(code, globs, locs)

只要存在, Python 就一直接受与 Python 3 兼容的“语法” 。这样做的原因是 Python 2 和 Python 1(?!)有一个技巧来保持向后兼容Python 0.9.8,其中是一个函数。现在,如果传递一个 2 元组,它被解释为,如果是一个 3 元组,它被解释为。是的,in是不必要的复杂。execexecexecexec(code, globals)(code, globals, locals)exec_six

因此,

exec(source, global_vars, local_vars)

保证在 CPython 0.9.9、1.x、2.x、3.x 中以相同的方式工作;而且我还验证了它在 Jython 2.5.2、PyPy 2.3.1 (Python 2.7.6) 和 IronPython 2.6.1 中有效:

Jython 2.5.2 (Release_2_5_2:7206, Mar 2 2011, 23:12:06) 
[Java HotSpot(TM) 64-Bit Server VM (Oracle Corporation)] on java1.8.0_25
Type "help", "copyright", "credits" or "license" for more information.
>>> exec('print a', globals(), {'a':42})
42

*) 存在细微差别,因此并非所有Python 3代码都适用于 Python 2,即

  • foo = exec在 Python 3 中有效,但在 Python 2 中无效map(exec, ['print(a + a)', 'print(b + b)']),但我真的不知道为什么有人想在实际代码中使用这些结构。
  • 正如Paul Hounshell所发现的,在 Python 2 中,以下代码将引发SyntaxError: unqualified exec is not allowed in function 'print_arg' 因为它包含一个带有自由变量的嵌套函数

    def print_arg(arg):
        def do_print():
            print(arg)
        exec('do_print()')
    

    以下构造毫无例外地工作。

    def print_arg(arg):
        def do_print():
            print(arg)
        exec 'do_print()' in {}
    

    在 Python 2.7.9 之前,如果将一个用于exec('do_print()', {})后者,则会抛出相同的 SyntaxError;但从 Python 2.7.9 开始,解析器/编译器也将允许这种备用元组语法。

同样,边缘情况下的解决方案可能是放弃使用execeval改为使用(eval可用于执行compileexec模式下编译的字节码):

def print_arg(arg):
    def do_print():
        print(arg)

    eval(compile('do_print(); print("it really works")', '<string>', 'exec'))

我已经写了一个关于内部结构的更详细的答案execeval以及Python 中的 eval、exec 和 compilecompile什么区别?

于 2015-04-26T10:34:24.707 回答
6

在 Antti 发布他的回答Python 2 support the Python 3 exec function syntax之前,我找到了几个这样做的选项。

第一个表达式也可以是长度为 2 或 3 的元组。在这种情况下,必须省略可选部分。形式exec(expr, globals)等价于exec expr in globals,而形式exec(expr, globals, locals)等价于exec expr in globals, locals。exec 的元组形式提供了与 Python 3 的兼容性,其中 exec 是一个函数而不是语句。

如果您出于某种原因不想使用它,这里是我找到的所有其他选项。

导入存根

您可以声明两个不同的导入存根并导入与当前解释器一起使用的任何一个。这是基于我在 PyDev 源代码中看到的。

这是您在主模块中放入的内容:

try:
    from exec_python2 import exec_code #@UnusedImport
except:
    from exec_python3 import exec_code #@Reimport

这是您放入的内容exec_python2.py

def exec_code(source, global_vars, local_vars):
    exec source in global_vars, local_vars

这是您放入的内容exec_python3.py

def exec_code(source, global_vars, local_vars):
    exec(source, global_vars, local_vars)

在 Eval 中执行

Ned Batchelder发布了一种将语句包装exec在调用中的技术,eval因此它不会在 Python 3 中导致语法错误。它很聪明,但不清楚。

# Exec is a statement in Py2, a function in Py3

if sys.hexversion > 0x03000000:
    def exec_function(source, filename, global_map):
        """A wrapper around exec()."""
        exec(compile(source, filename, "exec"), global_map)
else:
    # OK, this is pretty gross.  In Py2, exec was a statement, but that will
    # be a syntax error if we try to put it in a Py3 file, even if it isn't
    # executed.  So hide it inside an evaluated string literal instead.
    eval(compile("""\
def exec_function(source, filename, global_map):
    exec compile(source, filename, "exec") in global_map
""",
    "<exec_function>", "exec"
    ))

六包

是一个兼容性库,用于编写可在 Python 2 和 Python 3 下运行的代码。它具有可转换为两个版本的exec_()功能。我没试过。

于 2012-10-09T22:17:56.623 回答
1

我需要这样做,我不能使用六个,而且我的 Python 版本不支持 @Antti 的方法,因为我在带有自由变量的嵌套函数中使用了它。我也不想要不必要的进口。这就是我想出的。这可能需要在模块中,而不是在方法中:

try:
  # Try Python2.
  _exec_impls = {
    0: compile('exec code', '<python2>', 'exec'),
    1: compile('exec code in _vars[0]', '<python2>', 'exec'),
    2: compile('exec code in _vars[0], _vars[1]', '<python2>', 'exec'),
  }

  def _exec(code, *vars):
    impl = _exec_impls.get(len(vars))
    if not impl:
      raise TypeError('_exec expected at most 3 arguments, got %s' % (len(vars) + 1))
    return eval(impl, { 'code': code, '_vars': vars })

except Exception as e:
  # Wrap Python 3.
  _exec = eval('exec')

之后,_exec像 Python3 版本一样工作。你可以给它一个字符串,也可以通过compile(). 它不会得到你可能想要的全局变量或局部变量,所以将它们传入:

def print_arg(arg):
  def do_print():
    print(arg)
  _exec('do_print(); do_print(); do_print()', globals(), locals())

print_arg(7)  # Prints '7'

或不。我是 StackOverflow 的帖子,不是警察。

更新:

你为什么不直接使用eval()eval()期望表达式,而exec()期望语句。如果您刚刚得到一个表达式,那么使用什么并不重要,因为所有有效的表达式都是有效的语句,但反之则不成立。仅仅执行一个方法就是一个表达式,即使它没有返回任何东西;有一个隐含的None返回。

这通过尝试 eval 来证明pass,这是一个语句:

>>> exec('pass')
>>> eval('pass')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    pass
       ^
SyntaxError: unexpected EOF while parsing
于 2016-12-28T20:16:42.840 回答