如何从简单函数中提取名称?

10

我有这段代码:

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 internals: Working with Python ASTs

我获取了一个可工作的代码生成模块,在这里


1
你真正想要实现什么目标?一旦你有了这些名称,你需要做什么? - Ira Baxter
@Ira:我编辑了问题,提供了一些背景。 - Eddy Pronk
所以你真正想要实现的是将Python代码翻译成Excel公式。因此,你需要的不仅仅是名称;而是整个表达式的结构,即使它看起来很简单。我必须承认,我不明白为什么从一个体面的AST模块访问这些数据应该很困难。 - Ira Baxter
如果这些foo对象在您的控制之下,那么您可以通过简单的运算符重载实现所有这些。 - Anurag Uniyal
@Ira:我已经发布了一个回答,其中包含我目前得到的内容。我正在使用ast模块。 ast.NodeTransformer可以替换树的部分。 - Eddy Pronk
3个回答

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']

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。

谢谢。我在问题中添加了一些背景信息。 - Eddy Pronk

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))
            # 对full_name进行操作
        for c in node.getChildNodes():
            self.visit(c)

代码略有改动,可能会引入无意中的错误。我希望这能给你一个大致的想法:你需要访问Name和Getattr节点并构造点分名称,并处理你将看到所有中间值的事实(例如'foo'和'foo.bar')。


谢谢。我会尝试使用ast做同样的事情。 - Eddy Pronk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接