是否可以调用exec以使其与Python 3和Python 2兼容?

时间:2012-10-09 22:17:57

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

我在某些Python 2代码中使用exec语句,我正在尝试使该代码与Python 2和Python 3兼容,但在Python 3中,exec已更改从声明到函数。是否可以编写与Python 2和3兼容的代码?我读过有关Python 2 and Python 3 dual development的内容,但我对exec语句/函数更改的具体解决方案感兴趣。

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

3 个答案:

答案 0 :(得分:11)

有些Python porting guides get the exec 错误

  

如果您需要传入全局或本地词典,则需要使用两个不同的实现来定义自定义函数,一个用于Python 2,另一个用于Python 3.通常six包括一个很好的实现叫exec_()

将Python 2代码移植到Python 3 (*)中不需要这样的自定义函数。您可以在Python 2中执行exec(code)exec(code, globs)exec(code, globs, locs),它可以正常运行。

Python 始终接受Python 3兼容"语法"对于exec exec存在exec。这样做的原因是Python 2和Python 1(?!)有一个hack来与Python 0.9.8保持向后兼容,其中exec是一个函数。现在,如果(code, globals)传递了2元组,则它被解释为(code, globals, locals),如果是3元组,则将其解释为exec(source, global_vars, local_vars) 。是的,exec_ in six不必要地复杂化了。

因此,

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

保证在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:

foo = exec

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

  • map(exec, ['print(a + a)', 'print(b + b)'])在Python 3中有效但在Python 2中无效,def print_arg(arg): def do_print(): print(arg) exec('do_print()') 也是如此,但我真的不知道为什么有人想在实际代码中使用这些结构
  • 正如Paul Hounshell所发现的那样,在Python 2中,以下代码将引发SyntaxError: unqualified exec is not allowed in function 'print_arg' because it contains a nested function with free variables

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

    以下构造无例外地工作。

    exec('do_print()', {})

    在Python 2.7.9之前,如果使用exec替代后者,则会抛出相同的SyntaxError;但是从Python 2.7.9开始,解析器/编译器也会允许这种备用元组语法。

同样,边缘情况下的解决方案可能是放弃使用eval并使用def print_arg(arg): def do_print(): print(arg) eval(compile('do_print(); print("it really works")', '<string>', 'exec')) 代替eval can be used to execute bytecode that is compiled with compile in exec mode):

exec

我在What's the difference between eval, exec, and compile in Python?

上撰写了关于evalcompile using Word = Microsoft.Office.Interop.Word; public void Export_Data_To_Word(DataGridView DGV, string filename) { if (DGV.Rows.Count != 0) { int RowCount = DGV.Rows.Count; int ColumnCount = DGV.Columns.Count; Object[,] DataArray = new object[RowCount + 1, ColumnCount + 1]; //add rows int r = 0; for (int c = 0; c <= ColumnCount - 1; c++) { for (r = 0; r <= RowCount - 1; r++) { DataArray[r, c] = DGV.Rows[r].Cells[c].Value; } //end row loop } //end column loop Word.Document oDoc = new Word.Document(); oDoc.Application.Visible = true; //page orintation oDoc.PageSetup.Orientation = Word.WdOrientation.wdOrientLandscape; dynamic oRange = oDoc.Content.Application.Selection.Range; string oTemp = ""; for (r = 0; r <= RowCount - 1; r++) { for (int c = 0; c <= ColumnCount - 1; c++) { oTemp = oTemp + DataArray[r, c] + "\t"; } } //table format oRange.Text = oTemp; object Separator = Word.WdTableFieldSeparator.wdSeparateByTabs; object ApplyBorders = true; object AutoFit = true; object AutoFitBehavior = Word.WdAutoFitBehavior.wdAutoFitContent; oRange.ConvertToTable(ref Separator, ref RowCount, ref ColumnCount, Type.Missing, Type.Missing, ref ApplyBorders, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, ref AutoFit, ref AutoFitBehavior, Type.Missing); oRange.Select(); oDoc.Application.Selection.Tables[1].Select(); oDoc.Application.Selection.Tables[1].Rows.AllowBreakAcrossPages = 0; oDoc.Application.Selection.Tables[1].Rows.Alignment = 0; oDoc.Application.Selection.Tables[1].Rows[1].Select(); oDoc.Application.Selection.InsertRowsAbove(1); oDoc.Application.Selection.Tables[1].Rows[1].Select(); //header row style oDoc.Application.Selection.Tables[1].Rows[1].Range.Bold = 1; oDoc.Application.Selection.Tables[1].Rows[1].Range.Font.Name = "Tahoma"; oDoc.Application.Selection.Tables[1].Rows[1].Range.Font.Size = 14; //add header row manually for (int c = 0; c <= ColumnCount - 1; c++) { oDoc.Application.Selection.Tables[1].Cell(1, c + 1).Range.Text = DGV.Columns[c].HeaderText; } //table style oDoc.Application.Selection.Tables[1].set_Style("Grid Table 4 - Accent 5"); oDoc.Application.Selection.Tables[1].Rows[1].Select(); oDoc.Application.Selection.Cells.VerticalAlignment = Word.WdCellVerticalAlignment.wdCellAlignVerticalCenter; //header text foreach (Word.Section section in oDoc.Application.ActiveDocument.Sections) { Word.Range headerRange = section.Headers[Word.WdHeaderFooterIndex.wdHeaderFooterPrimary].Range; headerRange.Fields.Add(headerRange, Word.WdFieldType.wdFieldPage); headerRange.Text = "your header text"; headerRange.Font.Size = 16; headerRange.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter; } //save the file oDoc.SaveAs2(filename); //NASSIM LOUCHANI } } private void button_Click(object sender, EventArgs e) { SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = "Word Documents (*.docx)|*.docx"; sfd.FileName = "export.docx"; if (sfd.ShowDialog() == DialogResult.OK) { Export_Data_To_Word(dataGridView1, sfd.FileName); } } 内部的更详细的答案

答案 1 :(得分:6)

在Antti发布his answer之前,我找到了几个选项,Python 2支持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中引起语法错误。它很聪明,但不清楚。< / p>

# 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"
    ))

六个包

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

答案 2 :(得分: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返回。

通过尝试评估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