如何将未引用的Python函数/ lambda转换为AST?2.6
这看起来应该很简单,但我到处找都找不到答案,也自己想不出来。怎么把一个没有引号的Python函数或者lambda表达式转换成抽象语法树(AST)呢?
这是我想要做到的事情。
import ast
class Walker(ast.NodeVisitor):
pass
# ...
# note, this doesnt work as ast.parse wants a string
tree = ast.parse(lambda x,y: x+y)
Walker().visit(tree)
5 个回答
Meta库可以让你在很多情况下找回源代码,但有一些例外,比如列表推导式和匿名函数(也叫lambda)。
import meta, ast
source = '''
a = 1
b = 2
c = (a ** b)
'''
mod = ast.parse(source, '<nofile>', 'exec')
code = compile(mod, '<nofile>', 'exec')
mod2 = meta.decompile(code)
source2 = meta.dump_python_source(mod2)
assert source == source2
一般来说,你是做不到的。比如说,2 + 2
是一个表达式——但是如果你把它传给任何函数或方法,传进去的参数就只是数字 4
,你无法找回它是从哪个表达式计算出来的。函数的源代码有时可以找回(不过对于 lambda
函数就不行),但是“一个没有引号的 Python 表达式”会被计算,所以你得到的只是这个表达式的值。
你想解决什么问题呢?可能还有其他可行的方法。
编辑:感谢提问者的澄清。对于 lambda
或其他一些特殊情况,是没有办法做到的,但正如我提到的,函数的源代码有时可以找回……:
import ast
import inspect
def f():
return 23
tree = ast.parse(inspect.getsource(f))
print ast.dump(tree)
inspect.getsource
会在无法获取你传入对象的源代码时抛出 IOError
错误。我建议你把解析和获取源代码的调用放到一个辅助函数中,这个函数可以接受一个字符串(并进行解析)或者一个函数(并尝试获取源代码,这样在出现 IOError
的情况下可能会给出更好的错误信息)。
如果你只获取了函数或lambda表达式,你得到的只是编译后的Python字节码。因为在编译过程中会丢失一些信息,所以无法从字节码中重建出完整的Python抽象语法树(AST)。不过,你可以分析字节码并为其创建AST。在GeniuSQL中就有这样一个分析工具。我自己也做了一个小的概念验证,能够分析字节码并从中创建SQLAlchemy的条件元素。
我用来分析的过程如下:
- 把代码拆分成一个包含操作码和可能参数的列表。
- 通过查看操作码找到代码中的基本块,对于每个跳转,在跳转后和跳转目标前创建一个基本块的边界。
- 根据基本块创建一个控制流图。
- 通过抽象解释遍历所有基本块,跟踪栈和变量的赋值,使用SSA形式。
- 要生成输出表达式,只需获取计算出的SSA返回值。
我已经把我的 概念验证代码 和 使用它的示例代码 粘贴在这里。这些代码是快速拼凑出来的,不是很整洁,但如果你愿意,可以在此基础上进行开发。如果你决定从中做一些有用的东西,请留个言告诉我。