如何从简单函数中提取名称?
我有一段代码:
import inspect
import ast
def func(foo):
return foo.bar - foo.baz
s = inspect.getsource(func)
xx = ast.parse(s)
class VisitCalls(ast.NodeVisitor):
def visit_Name(self, what):
if what.id == 'foo':
print ast.dump(what.ctx)
VisitCalls().visit(xx)
我想从函数 'func' 中提取:
['foo.bar', 'foo.baz']
或者类似这样的东西:
(('foo', 'bar'), ('foo', 'baz))
编辑过了
一些背景信息,解释我为什么觉得需要这样做
我想把一个简单的 Python 函数的代码转换成电子表格公式。
所以我需要把:
foo.bar - foo.baz
转换成:
=A1-B1
示例电子表格 http://img441.imageshack.us/img441/1451/84516405.png
**再次编辑*
我目前的进展。
下面的程序输出:
('A1', 5)
('B1', 3)
('C1', '= A1 - B1')
这段代码:
import ast, inspect
import codegen # by Armin Ronacher
from collections import OrderedDict
class SpreadSheetFormulaTransformer(ast.NodeTransformer):
def __init__(self, sym):
self.sym = sym
def visit_Attribute(self, node):
name = self.sym[id(eval(codegen.to_source(node)))]
return ast.Name(id=name, ctx=ast.Load())
def create(**kwargs):
class Foo(object): pass
x = Foo()
x.__dict__.update(kwargs)
return x
def register(x,y):
cell[y] = x
sym[id(x)] = y
def func(foo):
return foo.bar - foo.baz
foo = create(bar=5, baz=3)
cell = OrderedDict()
sym = {}
register(foo.bar, 'A1')
register(foo.baz, 'B1')
source = inspect.getsource(func)
tree = ast.parse(source)
guts = tree.body[0].body[0].value
SpreadSheetFormulaTransformer(sym).visit(guts)
code = '= ' + codegen.to_source(guts)
cell['C1'] = code
for x in cell.iteritems():
print x
我在这里找到了一些资源: Python 内部机制:使用 Python ASTs 我在 这里 找到了一个可以用的代码生成模块。
3 个回答
0
我还没有使用新的ast模块,但我有一段可以工作的代码,它使用旧的compiler.ast来实现类似的功能:
def visitGetattr(self, node): full_name = [node.attrname] parent = node.expr while isinstance(parent, compiler.ast.Getattr): full_name.append(parent.attrname) parent = parent.expr if isinstance(parent, compiler.ast.Name): full_name.append(parent.name) full_name = ".".join(reversed(full_name)) # do something with full_name for c in node.getChildNodes(): self.visit(c)
这段代码稍微改了一下,可能引入了一些无意的错误。希望这能给你一个大概念:你需要访问Name和Getattr这两个节点,并构建带点的名称,同时还要处理你会看到所有中间值的事实(比如'foo'和'foo.bar')。
1
我不太明白你为什么需要获取名字。一个很简单的方法来获取函数中的所有名字和点是:
import inspect
import parser
import symbol
import token
import pprint
def func(foo):
return foo.bar - foo.baz
s = inspect.getsource(func)
st = parser.suite(s)
def search(st):
if not isinstance(st, list):
return
if st[0] in [token.NAME, token.DOT]:
print st[1],
else:
for s in st[1:]:
search(s)
search(parser.ast2list(st))
输出结果:
def func foo return foo . bar foo . baz
也许你可以通过更优雅地读取语法树来改进这个方法。我使用解析器而不是ast模块,因为我在用Python 2.5。
6
import ast, inspect
import codegen # by Armin Ronacher
def func(foo):
return foo.bar - foo.baz
names = []
class CollectAttributes(ast.NodeVisitor):
def visit_Attribute(self, node):
names.append(codegen.to_source(node))
source = inspect.getsource(func)
tree = ast.parse(source)
guts = tree.body[0].body[0].value
CollectAttributes().visit(guts)
print names
输出结果:
['foo.bar', 'foo.baz']