将 python 个对象转换为 python 个 AST 节点
Convert python objects to python AST-nodes
我需要将修改后的 python 对象转储回源中。所以我试图找到一些东西来将真正的 python 对象转换为 python ast.Node (稍后在 astor
库中使用以转储源)
我想要的用法示例,Python 2:
import ast
import importlib
import astor
m = importlib.import_module('something')
# modify an object
m.VAR.append(123)
ast_nodes = some_magic(m)
source = astor.dump(ast_nodes)
请帮我找到 some_magic
没有办法做你想做的事,因为那不是 AST 的工作方式。
当解释器运行你的代码时,它会从源文件中生成一个 AST,然后 解释 该 AST 以生成 python 对象。
这些对象生成后会发生什么与 AST 无关。
然而,有可能首先获得生成对象的 AST。
模块 inspect
可以让你获得一些 python 对象的源代码:
import ast
import importlib
import inspect
m = importlib.import_module('pprint')
s = inspect.getsource(m)
a = ast.parse(s)
print(ast.dump(a))
# Prints the AST of the pprint module
但 getsource()
的命名恰如其分。
如果我要更改 m
中某个变量(或任何其他对象)的值,它不会更改其源代码。
即使可以从对象中重新生成 AST,也不会有单一的解决方案 some_magic()
可以 return。
假设我在某个模块中有一个变量 x
,我在另一个模块中重新分配了它:
# In some_module.py
x = 0
# In __main__.py
m = importlib.import_module('some_module')
m.x = 1 + 227
现在,m.x
的值是 228
,但是没有办法知道什么样的表达式导致了那个值(好吧,不用阅读 __main__.py
的 AST 但是这很快就会失控)。仅仅是字面意思吗?函数调用的结果?
如果修改某个模块的某个值后确实需要得到一个新的AST,最好的办法是自己改造原来的AST。
你可以找到你的标识符从哪里获得它的价值,并用你想要的任何东西替换赋值的值。
例如,在我的小例子中 x = 0
由以下 AST 表示:
Assign(targets=[Name(id='x', ctx=Store())], value=Num(n=0))
为了让 AST 与我在 __main__.py
中所做的重新分配相匹配,我必须将上述 Assign
节点的值更改为以下内容:
value=BinOp(left=Num(n=1), op=Add(), right=Num(n=227))
如果您想那样做,我建议您查看 Python 的 AST 节点转换器文档 (ast.NodeTransformer), as well as this excellent manual that documents all the nodes you can meet in Python ASTs Green Tree Snakes - the missing Python AST docs.
Vladimir 所询问的内容对于编译器优化当然很有用。事实上,有一些方法可以使用 ast 库来实现。下面是一个演示常量函数求值的简单示例:
from ast import *
import numpy as np
PURE_FUNS = {'arange' : np.arange}
PROG = '''
A=arange(5)
B=[0, 1, 2, 3, 4]
A[2:3] = 1
C = [A[1], 2, m]
'''
def py_to_ast(o):
if type(o) == np.ndarray:
return List(elts=[py_to_ast(e) for e in o], ctx=Load())
elif type(o) == np.int64:
return Constant(value=o)
# Add elifs for more types here
else:
assert False
class EvalPureFuns(NodeTransformer):
def visit_Call(self, node):
is_const_args = all(type(a) == Constant for a in node.args)
if node.func.id in PURE_FUNS and is_const_args:
res = eval(unparse(node), PURE_FUNS)
return py_to_ast(res)
return node
node = parse(PROG)
node = EvalPureFuns().visit(node)
print(unparse(node))
我需要将修改后的 python 对象转储回源中。所以我试图找到一些东西来将真正的 python 对象转换为 python ast.Node (稍后在 astor
库中使用以转储源)
我想要的用法示例,Python 2:
import ast
import importlib
import astor
m = importlib.import_module('something')
# modify an object
m.VAR.append(123)
ast_nodes = some_magic(m)
source = astor.dump(ast_nodes)
请帮我找到 some_magic
没有办法做你想做的事,因为那不是 AST 的工作方式。 当解释器运行你的代码时,它会从源文件中生成一个 AST,然后 解释 该 AST 以生成 python 对象。 这些对象生成后会发生什么与 AST 无关。
然而,有可能首先获得生成对象的 AST。
模块 inspect
可以让你获得一些 python 对象的源代码:
import ast
import importlib
import inspect
m = importlib.import_module('pprint')
s = inspect.getsource(m)
a = ast.parse(s)
print(ast.dump(a))
# Prints the AST of the pprint module
但 getsource()
的命名恰如其分。
如果我要更改 m
中某个变量(或任何其他对象)的值,它不会更改其源代码。
即使可以从对象中重新生成 AST,也不会有单一的解决方案 some_magic()
可以 return。
假设我在某个模块中有一个变量 x
,我在另一个模块中重新分配了它:
# In some_module.py
x = 0
# In __main__.py
m = importlib.import_module('some_module')
m.x = 1 + 227
现在,m.x
的值是 228
,但是没有办法知道什么样的表达式导致了那个值(好吧,不用阅读 __main__.py
的 AST 但是这很快就会失控)。仅仅是字面意思吗?函数调用的结果?
如果修改某个模块的某个值后确实需要得到一个新的AST,最好的办法是自己改造原来的AST。
你可以找到你的标识符从哪里获得它的价值,并用你想要的任何东西替换赋值的值。
例如,在我的小例子中 x = 0
由以下 AST 表示:
Assign(targets=[Name(id='x', ctx=Store())], value=Num(n=0))
为了让 AST 与我在 __main__.py
中所做的重新分配相匹配,我必须将上述 Assign
节点的值更改为以下内容:
value=BinOp(left=Num(n=1), op=Add(), right=Num(n=227))
如果您想那样做,我建议您查看 Python 的 AST 节点转换器文档 (ast.NodeTransformer), as well as this excellent manual that documents all the nodes you can meet in Python ASTs Green Tree Snakes - the missing Python AST docs.
Vladimir 所询问的内容对于编译器优化当然很有用。事实上,有一些方法可以使用 ast 库来实现。下面是一个演示常量函数求值的简单示例:
from ast import *
import numpy as np
PURE_FUNS = {'arange' : np.arange}
PROG = '''
A=arange(5)
B=[0, 1, 2, 3, 4]
A[2:3] = 1
C = [A[1], 2, m]
'''
def py_to_ast(o):
if type(o) == np.ndarray:
return List(elts=[py_to_ast(e) for e in o], ctx=Load())
elif type(o) == np.int64:
return Constant(value=o)
# Add elifs for more types here
else:
assert False
class EvalPureFuns(NodeTransformer):
def visit_Call(self, node):
is_const_args = all(type(a) == Constant for a in node.args)
if node.func.id in PURE_FUNS and is_const_args:
res = eval(unparse(node), PURE_FUNS)
return py_to_ast(res)
return node
node = parse(PROG)
node = EvalPureFuns().visit(node)
print(unparse(node))