Come estrai i nomi da una funzione semplice?
-
13-09-2020 - |
Domanda
Ho questo pezzo di codice:
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)
.
Dalla funzione 'func' mi piacerebbe estrarre:
['foo.bar', 'foo.baz']
.
o qualcosa del genere come:
(('foo', 'bar'), ('foo', 'baz))
.
modificato
alcuni sfondo per spiegare perché penso di dover fare questo
Voglio convertire il codice di una funzione di pitone banale in una formula del foglio di calcolo.
Quindi ho bisogno di convertire:
foo.bar - foo.baz
.
A:
=A1-B1
.
Esempio di calcolo http://img441.imageshack.us/img441/1451/84516405. PNG
** Modificato di nuovo *
Cosa ho finora.
Il programma qui sotto le uscite:
('A1', 5)
('B1', 3)
('C1', '= A1 - B1')
.
Il codice:
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
.
Ho trovato alcune risorse qui: Python Internals: Lavorare con Python ASTS Ho afferrato un modulo di codegen funzionante qui .
Soluzione
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
.
Uscita:
['foo.bar', 'foo.baz']
. Altri suggerimenti
Non sono sicuro del motivo per cui hai bisogno di retirere i nomi, un modo molto grezzo per ottenere tutti i nomi e i punti in funzione è
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))
.
Uscita:
def func foo return foo . bar foo . baz
.
Potrebbe essere possibile migliorare su quello leggendo l'albero della sintassi più elegantemente, sto usando il parser invece del modulo AST perché sono su Python 2.5
Non ho ancora usato il nuovo modulo AST, ma ho il codice di lavoro che usa il vecchio compilatore .Ast per ottenere qualcosa di simile:
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).
Codice leggermente parafrasato, potrei aver introdotto bug involontario.Spero che questo ti dà l'idea generale: devi visitare sia il nome che i nodi Getattr e costruisci i nomi punteggiati e affrontare anche il fatto che vedrai anche tutti i valori intermedi (ad es. "FOO" e "FOO.BAR").