2016-08-01 10 views
0

Ich versuche gerade eine Python (3.4.4) GUI mit tkinter zu programmieren, die es erlauben sollte, eine beliebige Funktion an einige Datenpunkte anzupassen. Um einfach zu beginnen, möchte ich einige Eingabefunktionen erstellen und auswerten. Später möchte ich es plotten und passen curve_fit von scipy.Alternative zu exec

Um dies zu tun, möchte ich eine dynamische (Fitting) -Funktion aus einem Benutzer-Eingabe-String erstellen. Ich fand und las über exec, aber Leute sagen, dass (1) es nicht sicher zu verwenden ist und (2) es gibt immer eine bessere Alternative (z. B. here und an vielen anderen Orten). Also, ich fragte mich, was wäre die Alternative in diesem Fall?

Hier einige Beispiel-Code mit zwei verschachtelte Funktionen, die funktioniert, aber es ist nicht dynamisch:

def buttonfit_press(): 
    def f(x): 
     return x+1 
    return f 

print(buttonfit_press()(4)) 

Und hier ist ein Code, der zu NameError: name 'f' is not defined gibt, bevor ich auch verwenden xval starten:

def buttonfit_press2(xval): 
    actfitfunc = "f(x)=x+1" 
    execstr = "def {}:\n return {}\n".format(actfitfunc.split("=")[0], actfitfunc.split("=")[1]) 
    exec(execstr) 
    return f 

print(buttonfit_press2(4)) 

Ein alternativer Ansatz mit types.FunctionType hier diskutiert (10303248) war auch nicht erfolgreich ...

S o, meine Frage ist: Gibt es eine gute Alternative, die ich für dieses Szenario verwenden könnte? Oder wenn nicht, wie kann ich den Code mit exec laufen lassen?

Ich hoffe, es ist verständlich und nicht zu vage. Vielen Dank im Voraus für Ihre Ideen und Anregungen.


@ Gábor Erdos:

Entweder verstehe ich nicht oder ich nicht zustimmen. Wenn ich das gleiche Segment im mainloop kodieren, erkennt es f und ich kann das Code-Segment von execstr ausführen:

actfitfunc = "f(x)=x+1" 
execstr = "def {}:\n return {}\n".format(actfitfunc.split("=")[0], actfitfunc.split("=")[1]) 
exec(execstr) 
print(f(4)) 
>>> 5 

@ Łukasz Rogalski:

Printing execstr scheint gut zu mir:

Einrückungsfehler ist aufgrund meines Editors unwahrscheinlich, aber ich habe es überprüft - es ist in Ordnung. my_locals Einführung, es in exec und Druck in danach Aufruf zeigt:

{'f': <function f at 0x000000000348D8C8>} 

Allerdings habe ich immer noch NameError: name 'f' is not defined bekommen.


@ user3691475:

Ihr Beispiel ist sehr ähnlich wie mein erstes Beispiel. Aber das ist in meinem Verständnis nicht "dynamisch", d.h. man kann die Ausgabe der Funktion nicht ändern, während der Code läuft.


@Dunes:

Ich denke, dies in die richtige Richtung geht, danke.Allerdings verstehe ich noch nicht, wie ich diese Funktion im nächsten Schritt bewerten und nutzen kann. Was ich meine ist: Um es anpassen zu können, muss ich passende Variablen (d. H. a in f(x)=a*x+b) extrahieren oder die Funktion bei verschiedenen x-Werten auswerten (d. H. print(f(3.14))).

+0

@ GáborErdős technisch ist es, da exec implizit Verweise auf 'Globals() übergeben wird' und 'Einheimischen()' und Eingabeargumente, und sie werden mutiert werden. –

+0

@nostradamus Ihr Code sollte gut funktionieren. Ist Ihre Einrückung korrekt? Kannst du folgendes ausführen: 'my_locals = {}; exec (execstr, globals(), my_locals) und zeige danach, was in 'my_locals' gespeichert ist? Eine Überprüfung der Gesundheit durch Drucken von "execstr" wird ebenfalls nicht schaden. –

Antwort

0

Ich bin nicht sicher, was genau sind Sie versuchen zu tun, das heißt, welche Funktionen sind erlaubt, welche Operationen sind erlaubt, usw.

Hier ist ein Beispiel eines Funktionsgenerators mit einem dynamischen Parameter:

>>> def generator(n): 
     def f(x): 
      return x+n 
     return f 
>>> plus_one=generator(1) 
>>> print(plus_one(4)) 
5 
1

Das Problem mit exec/eval ist, dass sie beliebigen Code ausführen können. Um also exec oder eval zu verwenden, müssen Sie entweder das Code-Fragment sorgfältig analysieren, um sicherzustellen, dass es keinen schädlichen Code enthält (eine unglaublich schwere Aufgabe), oder Sie können sicher sein, dass die Quelle des Codes vertrauenswürdig ist. Wenn Sie ein kleines Programm für den persönlichen Gebrauch machen, dann ist das in Ordnung. Ein großes Programm, das für sensible Daten oder Geld verantwortlich ist, definitiv nicht. Es scheint, dass Ihr Anwendungsfall eine vertrauenswürdige Quelle hat.

Wenn Sie nur eine beliebige Funktion zur Laufzeit erstellen möchten, dann verwenden Sie einfach eine Kombination aus dem Lambda-Ausdruck und eval. z.B.

func_str = "lambda x: x + 1" # equates to f(x)=x+1 
func = eval(func_str) 
assert func(4) == 5 

Der Grund, warum Ihr Versuch nicht funktioniert, ist, dass locals(), im Rahmen einer Funktion, eine Kopie des lokalen Namespace erstellt. Mutationen im resultierenden Wörterbuch wirken sich nicht auf den aktuellen lokalen Namespace aus. Sie müssten wie etwas tun:

def g(): 
    src = """ 
def f(x): 
    return x + 1 
    """ 
    exec_namespace = {} # exec will place the function f in this dictionary 
    exec(src, exec_namespace) 
    return exec_namespace['f'] # retrieve f