【问题标题】:How do I extract the names from a simple function?如何从一个简单的函数中提取名称?
【发布时间】:2011-03-13 20:59:08
【问题描述】:

我有这段代码:

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

sample spreadsheet 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 我抓住了一个工作代码生成模块here

【问题讨论】:

  • 您真正想要完成什么?一旦你有了名字,你需要做什么?
  • @Ira:我编辑了问题以提供一些背景信息。
  • 所以你真正想要完成的是将python代码翻译成excel公式。因此,您需要的不仅仅是名称;它是大概微不足道的表达式的整个结构。我必须承认,我不明白为什么从一个像样的 AST 模块访问这些数据会很困难。
  • 并且是您控制下的 foo 对象,如果是,您可以通过简单的运算符重载来实现所有这些
  • @Ira:我发布了到目前为止所获得的答案。我正在使用 ast 模块。 ast.NodeTransformer 可以替换部分树。

标签: python abstract-syntax-tree codegen


【解决方案1】:
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']

【讨论】:

    【解决方案2】:

    我还没有使用新的 ast 模块,但我正在使用旧的 compiler.ast 代码来实现类似的功能:

    def visitGetattr(自我,节点): full_name = [node.attrname] 父 = node.expr 而 isinstance(父级,compiler.ast.Getattr): full_name.append(parent.attrname) 父=父.expr 如果是实例(父,compiler.ast.Name): full_name.append(parent.name) full_name = ".".join(reversed(full_name)) # 用 full_name 做一些事情 对于 node.getChildNodes() 中的 c: self.visit(c)

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

    【讨论】:

    • 谢谢。我会尝试对 ast 做同样的事情。
    【解决方案3】:

    我不确定你为什么需要检索名称,在函数中获取所有名称和点的一种非常粗略的方法是

    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 上

    【讨论】:

    • 谢谢。我在问题中添加了一些背景信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-05
    • 2018-07-02
    • 2012-08-28
    • 2020-08-19
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    相关资源
    最近更新 更多