Wie macht man ein Python 'eval' nur innerhalb eines Objektkontextes?

8

Ist es möglich, etwas wie

zu tun? %Vor%

in Python..i.e. Haben func1 () und func2 () im Rahmen des Objekts 'c' ausgewertet (wenn sie Mitgliedfunktionen innerhalb dieser Klassendefinition waren)? Ich kann kein einfaches Parsing machen, da die eval-Strings für meine Anwendung beliebig kompliziert werden können. Ich denke, ein bisschen Magie mit dem ast-Modul könnte den Trick machen, aber aufgrund der Menge an Literatur in ast, bin ich mir nicht sicher, wo ich hinschauen soll:

%Vor%     
Vineet Bansal 17.12.2012, 22:10
quelle

4 Antworten

9

Sie wollen das fast sicher nicht, aber Sie können .

Der Kontext für eval ist die globale und lokale Wörterbücher, die Sie Ihren Code auswerten möchten Die häufigsten Fälle sind wahrscheinlich eval(expr, globals(), mycontext) und eval(expr, mycontext) , die den lokalen bzw. globalen Standardkontext ersetzen und den anderen in Ruhe lassen. Das Ersetzen des lokalen Kontexts durch das Wörterbuch eines Objekts ähnelt dem Ausführen von "innerhalb" (einer Methode) dieses Objekts. Beachten Sie jedoch, dass "eine Mitgliedsfunktion zu sein" nicht so gut ist, wie Sie vielleicht erwarten würden Haben Sie self , um andere Memberfunktionen auf ...

aufzurufen

Wie auch immer, hier ist ein kurzes Beispiel:

%Vor%

Beachten Sie, dass __dict__ hier möglicherweise nicht genau das ist, was Sie wollen. Zum Beispiel:

%Vor%

Damit dies so funktioniert, wie Sie es wollen, müssen Sie genau wissen, wie Sie das wollen, in Python-Begriffen - was ein bisschen darüber wissen muss, wie Objekte unter den Deckeln funktionieren (MRO, vielleicht Deskriptoren, etc.) / p>

Wenn Sie wirklich eval benötigen und wirklich beliebige Kontexte bereitstellen müssen, ist es wahrscheinlich besser, diese Kontexte explizit (als Wörterbücher) zu erstellen, anstatt zu versuchen, Objekte in diese Rolle zu zwingen:

%Vor%

Diese Verwendung ist viel näher an dem JavaScript-Stil, den Sie in Python trotzdem emulieren möchten.

Im Gegensatz zu JS lässt Python natürlich keine mehrzeiligen Definitionen innerhalb eines Ausdrucks zu, so dass Sie dies für komplexe Fälle tun müssen:

%Vor%

Aber das ist wohl fast immer lesbarer (was im Grunde das Argument dafür ist, dass Python keine mehrzeiligen Definitionen in Ausdrücken zulässt).

    
abarnert 17.12.2012, 22:39
quelle
1

Also, ich rate dir, so etwas zu tun:

%Vor%

Scheint so, als ob Sie brauchen. Lassen Sie mich erklären, wie das funktioniert.

  1. eval akzeptiert Locals und Globals als Parameter.
  2. Wir müssen also einen speziellen globalen Kontext definieren, der eine "Repräsentation" Ihrer Klasse darstellt.
  3. Dazu müssen wir als globals -Wörterbuch alle "wertvollen" begrenzten Methoden bereitstellen.
  4. Ausgehend von Simples Teil. Wir haben S -Klassendefinition. Wie bekommt man alle "wertvollen" Methoden? Simple filter Namen von S.__dict__ , um zu überprüfen, ob der Methodenname von __ beginnt oder nicht (Sie sehen, dass wir als Ergebnis eine Liste mit 1 Element erhalten - add function).
  5. Erzeuge target = Instanz von S class, die "eval context" sein wird.
  6. Nächster Schritt ist am "schwierigsten". Wir müssen für jede Funktion eine "gebundene Methode" erstellen. Um dies zu tun, verwenden wir die Tatsache, dass die Klasse __dict__ Funktionen speichert, jede Funktion ist kein Datendekriptor und die gebundene Methode kann einfach mit func.__get__(obj, type(obj)) abgerufen werden. Dieser Vorgang wird in map ausgeführt.
  7. Nehmen Sie das Ergebnis aus dem vorherigen Schritt, erstellen Sie dict daraus.
  8. Übergeben Sie als globals an eval function.

Ich hoffe, das wird helfen.

    
Alexey Kachayev 17.12.2012 22:48
quelle
1

Die oben vorgeschlagene Lösung zum Auffüllen von locals funktioniert in den meisten Fällen gut, kann aber bei Eigenschaften (Datendeskriptoren) problematisch sein. Diese werden einmal bewertet , wenn das Wörterbuch gefüllt ist. Dies bedeutet, dass mehrere Verweise auf denselben Variablennamen immer die exakt gleiche Instanz zurückgeben, was bei Eigenschaften nicht erwartet werden kann.

Dieses Problem kann dadurch gelöst werden, dass eval ein locals Argument erwartet, das sich wie ein dict verhält (im Gegensatz zu globals, die sein müssen ) ein Code%). Mit anderen Worten, wir können dict in Ihrer Instanz überschreiben, um Variablennamen im Kontext der Instanz spontan aufzulösen und sie direkt als __getitem__ -Attribut an locals weiterzuleiten. Ihr Beispiel kann so implementiert werden als:

%Vor%

Dieser Trick sollte mit Vererbung, statischen Methoden, Klassenmethoden und Eigenschaften funktionieren. Schließlich verhindert die Verwendung von eval und dir die direkte Interaktion mit getattr oder __dict__ , obwohl die Ergebnisse von __mro__ möglicherweise .

    
nrtc 16.10.2015 18:59
quelle
0

Sie können sich die akzeptierte Antwort auf diese Frage ansehen: " Abrufen des Befehlsblocks, der in der with-Anweisung ausgeführt werden soll .

Dies war eine hilfreiche Möglichkeit für mich, eigene Kontexte zu erstellen, in denen mathematische Operationen auf rechteckigen Arrays, wie Python Pandas-Datenrahmen, "einfach funktionieren", ohne sich mit der hässlichen zusätzlichen Pandas-Syntax herumschlagen zu müssen. Wenn ich zum Beispiel " a = x*y " in den Kontext schreibe, weist es dem Kontextobjekt automatisch a als Attribut zu und weiß, dass Vektoroperationen mit den Attributen x und y des Kontextobjekts ausgeführt werden.

Ich habe diesen Kontext als sehr hilfreich empfunden, obwohl ich, wenn ich auf StackOverflow frage, oft die Antwort bekomme, dass es nicht unbedingt das ist, was ich wirklich machen möchte.

Dies könnte wahrscheinlich für den Kontext funktionieren, in dem eval auch nach Funktionen sucht.

    
ely 17.12.2012 22:46
quelle