在模块的帮助下,解析和修改代码结构当然是可能的,ast
稍后我将在一个示例中展示它。ast
但是,仅使用模块是不可能写回修改后的源代码的。还有其他模块可用于此工作,例如此处的一个。
注意:下面的示例可以被视为关于模块使用的介绍性教程,ast
但更全面的模块使用指南ast
可在Green Tree snakes 教程和模块的官方文档中ast
找到。
简介ast
:
>>> import ast
>>> tree = ast.parse("print 'Hello Python!!'")
>>> exec(compile(tree, filename="<ast>", mode="exec"))
Hello Python!!
您可以通过简单地调用 API 来解析 python 代码(以字符串表示)ast.parse()
。这将返回抽象语法树 (AST) 结构的句柄。有趣的是,您可以编译回这个结构并如上所示执行它。
另一个非常有用的 API 是以ast.dump()
字符串形式转储整个 AST。它可以用来检查树形结构,对调试很有帮助。例如,
在 Python 2.7 上:
>>> import ast
>>> tree = ast.parse("print 'Hello Python!!'")
>>> ast.dump(tree)
"Module(body=[Print(dest=None, values=[Str(s='Hello Python!!')], nl=True)])"
在 Python 3.5 上:
>>> import ast
>>> tree = ast.parse("print ('Hello Python!!')")
>>> ast.dump(tree)
"Module(body=[Expr(value=Call(func=Name(id='print', ctx=Load()), args=[Str(s='Hello Python!!')], keywords=[]))])"
请注意 Python 2.7 与 Python 3.5 中 print 语句的语法差异以及各自树中 AST 节点类型的差异。
如何修改代码使用ast
:
现在,让我们看一个按ast
模块修改python代码的例子。修改AST结构的主要工具是ast.NodeTransformer
类。每当需要修改 AST 时,他/她需要从中继承子类并相应地编写节点转换。
对于我们的示例,让我们尝试编写一个简单的实用程序,将 Python 2 、 print 语句转换为 Python 3 函数调用。
打印语句到 Fun 调用转换器实用程序:print2to3.py:
#!/usr/bin/env python
'''
This utility converts the python (2.7) statements to Python 3 alike function calls before running the code.
USAGE:
python print2to3.py <filename>
'''
import ast
import sys
class P2to3(ast.NodeTransformer):
def visit_Print(self, node):
new_node = ast.Expr(value=ast.Call(func=ast.Name(id='print', ctx=ast.Load()),
args=node.values,
keywords=[], starargs=None, kwargs=None))
ast.copy_location(new_node, node)
return new_node
def main(filename=None):
if not filename:
return
with open(filename, 'r') as fp:
data = fp.readlines()
data = ''.join(data)
tree = ast.parse(data)
print "Converting python 2 print statements to Python 3 function calls"
print "-" * 35
P2to3().visit(tree)
ast.fix_missing_locations(tree)
# print ast.dump(tree)
exec(compile(tree, filename="p23", mode="exec"))
if __name__ == '__main__':
if len(sys.argv) <=1:
print ("\nUSAGE:\n\t print2to3.py <filename>")
sys.exit(1)
else:
main(sys.argv[1])
可以在小示例文件上试用此实用程序,例如下面的一个,它应该可以正常工作。
测试输入文件:py2.py
class A(object):
def __init__(self):
pass
def good():
print "I am good"
main = good
if __name__ == '__main__':
print "I am in main"
main()
请注意,上述转换仅用于ast
教程目的,在实际情况下,您必须查看所有不同的场景,例如print " x is %s" % ("Hello Python")
.