¿Cómo extrao los nombres de una función simple?
-
13-09-2020 - |
Pregunta
Tengo este pedazo de código:
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)
de la función 'FUNC' me gustaría extraer:
['foo.bar', 'foo.baz']
o algo así como:
(('foo', 'bar'), ('foo', 'baz))
editado
Algunos antecedentes para explicar por qué creo que necesito hacer esto
Quiero convertir el código de una función trivial de python a una fórmula de hoja de cálculo.
Así que necesito convertir:
foo.bar - foo.baz
a:
=A1-B1
hoja de cálculo de muestra http://img441.imageshack.us/img441/1451/84516405. PNG
** editado de nuevo *
Lo que tengo hasta ahora.
El programa a continuación. Salidas:
('A1', 5)
('B1', 3)
('C1', '= A1 - B1')
El código:
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
Encontré algunos recursos aquí: Python Internals: Trabajando con Python ASTS Agarré un módulo de codegen de trabajo aquí .
Solución
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
Salida:
['foo.bar', 'foo.baz']
Otros consejos
No estoy seguro de por qué necesita retirar los nombres, una forma muy cruda de obtener todos los nombres y los puntos en función es
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))
Salida:
def func foo return foo . bar foo . baz
Puede ser que puede mejorar eso al leer el árbol de sintaxis más elegante, estoy usando analizador en lugar del módulo AST AST porque estoy en Python 2.5
Todavía no he usado el nuevo módulo AST, pero tengo un código de trabajo que utiliza el compilador anterior.ast para lograr algo similar:
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)
Código ligeramente desfavorecido, es posible que haya introducido errores involuntarios.Espero que esto le dé la idea general: debe visitar los nodos de Nombre y Getattr y construir nombres de puntos, y también lidiar con el hecho de que también verá todos los valores intermedios (por ejemplo, 'FOO' y 'FOO.BAR').