Es ist kein Problem, einen Konverter zu schreiben Ich werde einen kleinen Testinhalt schreiben. Zur Erinnerung, ich berühre Python selten.
Wenn beispielsweise die Zeichenfolge "v * 2" angegeben wird, möchte ich eine Funktion generieren, die das Argument v verdoppelt und zurückgibt. (Es ist bekannt, dass der Name des Arguments v ist)
Wie im obigen Beispiel ist es sehr einfach, wenn die angegebene Zeichenfolge eine einzelne Anweisung ist. Verwenden Sie einfach eine Zeichenfolgenoperation, um sie zu einem Lambda zu machen und zu bewerten.
single.py
text = "v * 2"
func = eval("lambda v:" + text)
Die obige Methode kann nicht verwendet werden, wenn die angegebene Zeichenfolge mehrere Ausdrücke enthalten kann. Zum Beispiel
from System.Windows import Thickness
Thickness(v, 0, 0, 0)
Betrachten Sie den Fall, in dem angegeben ist. Derzeit fällt mir nur ein, eine String-Operation zu verwenden, um eine Funktion def zu erstellen und diese dann auszuführen. (Dieser Code gibt für die generierte Funktion nur None zurück, aber darüber werde ich später sprechen.)
multi.py
text = """
from System.Windows import Thickness
Thickness(v, 0, 0, 0)
"""
import re
source = "def test(v):\n" + re.sub("^", " ", text, flags=re.M)
exec(source)
return test
Dies ist in den meisten Fällen in Ordnung, aber wenn beispielsweise ein mehrzeiliges Literal angegeben wird, besteht das Problem, dass der Inhalt des Literals durch die einfache Einrückungsverarbeitung durch den obigen regulären Ausdruck eingerückt wird. ..
"""value of v is:
{v}""".format(v=v)
Es ist ein seltener Fall, den Sie nicht wirklich berücksichtigen müssen, aber es ist etwas unangenehm zu wissen, dass es ein schlechtes Muster gibt. Als Alternative machen wir es zu einer Funktion, die durch Ast-Konvertierung defekt ist.
Der Ablauf.
Es sieht so aus, wenn es in Code geschrieben ist.
functionize.py
import ast
class _MakeFunction(ast.NodeTransformer):
def __init__(self, body):
self.body = body
def run(self):
template_ast = ast.parse('def test(v): pass', mode='exec')
return self.visit(template_ast)
def visit_Pass(self, node):
return ast.parse(self.body, mode='exec').body
if __name__ == '__main__':
body = "from System.Windows import Thickness\nThickness(v, 0, 0, 0)"
functionized_ast = _MakeFunction(body).run()
exec(compile(functionized_ast, '<string>', mode='exec'))
Jetzt ist es eine Funktion, aber wie ich zuvor geschrieben habe, gibt es keinen Rückgabewert wie er ist. Schreiben Sie die ast-Konvertierung also so um, dass das Ergebnis des zuletzt ausgewerteten Ausdrucks wie Ruby als Rückgabewert zurückgegeben wird. Das Verfahren ist wie folgt.
_retval_ = None
hinzureturn _retval_
hinzu_retval_
neu.Schreiben wir einen Transformator, der dies im Code tut.
functionize.py
class _ReturnLastResult(ast.NodeTransformer):
def visit_FunctionDef(self, node):
self.generic_visit(node)
retval_assign = \
ast.Assign(targets=[ast.Name(id='_retval_', ctx=ast.Store())],
value=ast.NameConstant(value=None))
retval_return = \
ast.Return(value=ast.Name(id='_retval_', ctx=ast.Load()))
node.body.insert(0, retval_assign)
node.body.append(retval_return)
return ast.fix_missing_locations(node)
def visit_Expr(self, node):
target = ast.Name(id='_retval_', ctx=ast.Store())
assign = ast.copy_location(
ast.Assign(targets=[target], value=node.value), node)
return assign
Der endgültige Code sieht so aus. https://gist.github.com/wonderful-panda/8a22b74248a60cc8bb22
Immerhin habe ich mich entschlossen, nur Single Statement im ursprünglichen Eintrag zu berücksichtigen und habe dies nicht verwendet.
Recommended Posts