我在某些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。
答案 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?
上撰写了关于eval
,compile
和 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