2010-11-11 14 views
8

So habe ich verstanden, was exec und eval und auch compile tun. Aber warum sollte ich sie benutzen müssen? Ich bin unklar über das Nutzungsszenario.Verwendung von exec und eval in Python

Kann jemand mir einige Beispiele geben, damit ich das Konzept besser schätzen kann. Denn ich weiß, es ist alles Theorie.

+3

Fakt: Dinge wie 'eval' sind sehr selten eine gültige Wahl, und nur dann, wenn die ihr zugeführte Zeichenkette als sicher bekannt ist. – delnan

+4

Versuchen Sie, in den Python-Quellcodedateien in der Python-Standardbibliothek nach ihnen zu suchen. Das ist immer ein interessanter Ausgangspunkt, wenn Sie die grundlegenden Python-Funktionen sehen möchten. –

+0

'exec' und' eval' können in [codegolfing] sehr nützlich sein (https://codegolf.stackexchange.com/). – Wrzlprmft

Antwort

5

Ich gebe ein Beispiel, in dem ich eval verwendet habe und wo ich denke, es war die beste Wahl.

Ich schrieb ein einfaches Software-Testprogramm ... etwas, um zu testen, ob die Übungen der Schüler den Anforderungen der Aufgaben entsprechen.Das Ziel bestand darin, eine einfache Konfigurationsdatei als Testspezifikation bereitzustellen (um eine "Henne-und-Ei" -Problematik der Verwendung einer Programmiersprache zur Beschreibung/Dokumentation/Implementierung der Testfälle für elementare Programmieraufgaben zu umgehen). .

Ich basierte mein Kabelbaum auf dem ConfigParser in den Standardbibliotheken. Ich wollte jedoch die Möglichkeit, beliebige Python-Zeichenfolgen (einschließlich Interpolationen von \ n, \ t und insbesondere interpolierte hexadezimale ASCII-Zeichen in den daraus gelesenen Werten darzustellen).

Meine Lösung war eine try um eine parsed_string=eval('''%s''' % cfg_read_item) gefolgt von a try der dreifachen doppelt zitierten Version ("" "% s" "") derselben.

Dies ist ein Fall, in dem die Alternative gewesen wäre, einen Python-Parser zu schreiben (oder einen vorgefertigten Python-Parser zu finden) und ich finde heraus, wie ich es in mein Programm einbinden und anpassen kann, die Risiken sind minimal (ich mache mir keine Sorgen, dass der Student Code eingibt, wird meinen Parser austricksen, ausbrechen, alle meine Dateien löschen, meine Kreditkartennummern senden nach Rumänien und so weiter) *

* (Zum Teil, weil ich sie unter Linux von einem nicht vertrauenswürdigen kopflosen Benutzerkonto getestet habe).

Wie hier bereits erwähnt, gibt es andere Anwendungsfälle, in denen Sie Code aus einer Vorlage basierend auf Eingabedaten erstellen und diesen Code ausführen müssen (Metaprogrammierung). Sie sollten diese Aufgaben immer auf eine andere Weise erledigen können. Wenn jedoch diese Alternative einen Programmieraufwand mit sich bringt, der sich an das Schreiben eines allgemeinen Programmiersprachenparsers/-kompilierers/-interpreters annähert, dann kann eval der bessere Ansatz sein.

0

Fragen Sie nur nach einem Beispiel? Sie könnten eine einfache App schreiben, die von Standard in liest und dem Benutzer erlaubt verschiedene Ausdrücke einzugeben, wie (4*2)/8 - 1. In anderen Sprachen (Java, C++, etc.) würde dies nahezu unmöglich sein, zu beurteilen, aber in Python ist es einfach, nur:

eval((4*2)/8 - 1) 

aber sagen, dass, wenn Sie vorsichtig sind, können diese Dinge verwenden sein sehr gefährlich, da sie (im Wesentlichen) dem Benutzer eine große Menge an Zugriff ermöglichen.

+1

Nicht "fast unmöglich zu bewerten" --- Python selbst wird meistens in C geschrieben und Python kann solche Ausdrücke auswerten. Besser zu sagen, dass die Auswertung von Ausdrücken ungefähr so ​​viel Arbeit wie das Schreiben eines Programmiersprachen-Parsers und Compilers oder Interpreters mit sich bringen würde. –

+1

@ Jim Dennis. Einverstanden, dass es nicht "fast unmöglich" ist. Es ist machbar für ein paar Stunden, vielleicht sogar weniger. Aber es ist ein Schmerz. – nosirrahcd

0

Dies wird in Meta-Programmierung verwendet (wenn das Programm selbst schreibt). Zum Beispiel haben Sie Tiere verschiedener Arten, die mit verschiedenen Klassen beschrieben werden: Löwe, Tiger, Pferd, Esel. Und Sie wollen die Kreuzung zwischen ihnen simulieren, zum Beispiel zwischen Löwen und Tiger. Wenn Sie das Programm schreiben, können Sie nicht bestimmen, wie der Benutzer die Tiere überqueren, aber Sie können neue Klassen von Tieren on the fly erstellen:

new_class_name = boy.class.to_str() + girl.class.to_str() 
eval("class " + new_class_name + " extends " + boy.class.to_str() + ", " + girl.class.to_str()) 

P. S. Sorry, ich einige Python vergessen. Es gibt also eine Menge Pseudo-Code.

+4

Nein, dies ist keine gültige Verwendung von 'eval' - Sie sollten stattdessen' type' verwenden. – katrielalex

1

Sie brauchen nicht, um sie zu verwenden, und meiner Meinung nach sollten Sie sie vermeiden.

Sie sind nur nützlich in Fällen, in denen Sie den Code selbst generieren, der am Ende wahrscheinlich als schlechte Praxis angesehen wird.

Wenn Sie in Erwägung ziehen, eval() für Dinge wie mathematische Ausdrücke zu verwenden, sollten Sie die Eingabe vor der Auswertung besser bereinigen. Sie wissen nie, welche Art von "Text" der Benutzer sendet, die die Anwendung selbst vermasseln könnte.

+5

"Meta-Programmierung" gleich "schlechte Praxis" zu haben ist ein bisschen hart, IMHO. Eine Meta-Programmierung, die manchmal komplexer und fehleranfälliger ist, kann bei richtiger Verwendung einen besseren (d. H. Schnelleren oder besser wartbaren) Code ergeben. – Kos

+1

Meta-Programmierung ist keine schlechte Praxis. Es kann sehr schön sein. –

+0

Jedes Werkzeug und jede Methode hat ihren Anwendungsbereich.Aber Sie haben recht, der Bereich der Anwendung der Metaprogrammierung überhaupt ist sehr eng. –

4

Die Standardbibliothek enthält ein instruktives Beispiel für die Verwendung von exec. collections.namedtuple verwendet es, um eine Klasse dynamisch zu erstellen.

template = '''class %(typename)s(tuple): 
    '%(typename)s(%(argtxt)s)' \n 
    __slots__ =() \n 
    _fields = %(field_names)r \n 
    def __new__(_cls, %(argtxt)s): 
     'Create new instance of %(typename)s(%(argtxt)s)' 
     return _tuple.__new__(_cls, (%(argtxt)s)) \n 
    ...''' 

    namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, 
        OrderedDict=OrderedDict, _property=property, _tuple=tuple) 
    try: 
     exec template in namespace 
    except SyntaxError, e: 
     raise SyntaxError(e.message + ':\n' + template) 
+2

Apropos verrücktes Programmieren ... – Unode

+0

Ich würde das wahrscheinlich mit einer Metaklasse machen –

+0

mit Funktionen höherer Ordnung –

3

ast verwendet compile abstrakte Syntaxbäume zu erzeugen, aus Python-Quellcode. Diese werden von Modulen wie pyflakes verwendet, um Python zu analysieren und zu validieren.

def parse(expr, filename='<unknown>', mode='exec'): 
    """ 
    Parse an expression into an AST node. 
    Equivalent to compile(expr, filename, mode, PyCF_ONLY_AST). 
    """ 
    return compile(expr, filename, mode, PyCF_ONLY_AST) 
0

Hier ist ein gültiger Anwendungsfall. In der Python-Paste-Middleware (für Web-Programmierung) erzeugt eine Exception eine Befehlszeile im Browser. Dies funktioniert mit Methoden wie diesen. In Blender gibt es auch eine Option, um Werte mithilfe von Python-Ausdrücken zu animieren. Dies funktioniert mit eval.

1

Ich denke, ich habe eine gültige Verwendung. Ich benutze Python 3.2.1 in Blender 2.6.4, um eine Menge von Punkten mit x, y Koordinaten (in der z-Ebene) zu modifizieren.

Das Ziel besteht darin, um jeden vorhandenen Punkt herum konzentrische Ringe mit neuen Punkten hinzuzufügen, wobei sich die Ringe wie Wellen verhalten (wie wenn man einen Stein in einen Teich fallen lässt). Der Clou ist, dass ich möchte, dass die Wellen konstruktiv/destruktiv ineinander eingreifen, also gehe ich zuerst durch und konstruiere eine "Welligkeitsgleichung", die an jedem Punkt zentriert ist, und summiere alle Welligkeitsgleichungen zu einer gigantischen mathematischen Gleichung Ich füge dann die ursprünglichen Punkte ein, um den korrekten Z-Wert zu erzeugen, dem ich jeden zuweisen kann.

Mein Plan ist es, jeden zusätzlichen Begriff in der Gleichung an die vorherige als Zeichenfolge anzuhängen und dann eval() zu verwenden, um den Z-Wert für die neue Menge von Punkten zu berechnen.