Was macht Pythons eval ()?


306

In dem Buch, das ich über Python lese, wird weiterhin der Code verwendet eval(input('blah'))

Ich habe die Dokumentation gelesen und verstehe sie, sehe aber immer noch nicht, wie sie die input()Funktion verändert.

Was tut es? Kann jemand erklären?


4
Die Eval-Funktion versucht, die an sie übergebene Zeichenfolge (Argument) als Python-Code auszuführen und zu interpretieren. x = 1 print (eval ('x + 1')) Die Ausgabe des obigen Codes ist 2. Der Nachteil eines solchen Ansatzes besteht darin, dass der Benutzer unabhängig vom Schreiben von Code wird, was zu verheerenden Bedingungen führen kann Zugriff auf viele Variablen und Methoden durch Übergabe globaler und lokaler Parameter in der Bewertungsfunktion.
ATIF IBAD KHAN

Antworten:


276

Mit der eval-Funktion kann ein Python-Programm Python-Code in sich selbst ausführen.

Bewertungsbeispiel (interaktive Shell):

>>> x = 1
>>> eval('x + 1')
2
>>> eval('x')
1

25
haha, das war ein triviales Beispiel, aber Sie könnten den Benutzer einen beliebigen Befehl eingeben lassen und ihn von Python ausführen lassen. Sie können also den Benutzer eine Befehlszeichenfolge eingeben lassen und sie dann von Python als Code ausführen lassen. Zum Beispiel: eval ("__ import __ ('os'). Remove ('file')").
BYS2

60
Es wird nutzlos erscheinen, bis Sie eine Notwendigkeit dafür finden. Es wird auf Websites wie codepad.org verwendet, damit Sie Skripte in einer Testumgebung ausführen können. eval()kann auch zum Ausführen von hochdynamischem Code verwendet werden. Sie sollten sich jedoch vor der Verwendung der Sicherheits- und Leistungsrisiken voll bewusst sein.
George Cummins

6
@GeorgeCummins, codepad.org verwendet es nicht evalund kann auch nicht das tun, was es tut eval.
Mike Graham

16
@GeorgeCummins: codepag.org führt alles in einer Sandbox aus: Ein Chroot-Gefängnis mit ptrace-Checks in einer virtuellen Maschine, um zu verhindern, dass bösartiger Code etwas Schlechtes tut. Weitaus komplizierter als eine einfache Bewertung. Außerdem ist eval Python-spezifisch. Das Codepad unterstützt eine Reihe von Sprachen.
FogleBird

4
@GeorgeCummins, Codepad führt ein sehr komplexes System aus, um beliebige Programme sicher auszuführen. evalAbgesehen davon, dass es unsicher ist, können nicht ganze Programme wie Codepad ausgeführt werden, da nur ein einziger Ausdruck ausgewertet werden kann.
Mike Graham

165

eval()interpretiert eine Zeichenfolge als Code. Der Grund, warum so viele Leute Sie davor gewarnt haben, ist, dass ein Benutzer dies als Option verwenden kann, um Code auf dem Computer auszuführen. Wenn Sie haben eval(input())und osimportiert haben, könnte eine Person eingeben, in input() os.system('rm -R *')die alle Ihre Dateien in Ihrem Home-Verzeichnis gelöscht werden. (Vorausgesetzt, Sie haben ein Unix-System). Verwenden eval()ist eine Sicherheitslücke. Wenn Sie Zeichenfolgen in andere Formate konvertieren müssen, versuchen Sie, Dinge zu verwenden, die dies tun, wie z int().


14
Sie meinen, die Verwendung evalmit input()ist eine Sicherheitslücke. Geben Sie keine input()Bewertung ein und es wird Ihnen gut gehen.
Rohmer

19
@Rohmer, unsichere Daten können von überall her kommen: Webanforderungen, Formulareingabefelder, Dateilesevorgänge, ... nicht nur von der Konsoleneingabe. Selbst wenn Sie die Dateien selbst schreiben, kann sie Eingaben enthalten, die ursprünglich von einer nicht vertrauenswürdigen Quelle stammen. Dies evalist in vielen Fällen ein Sicherheitsproblem.
Sanderd17

3
da inputder Benutzer normalerweise seine Daten von der Konsole bezieht, kann er das Programm einfach beenden und rm -R *trotzdem
eingeben

63

Viele gute Antworten hier, aber keine beschreiben die Verwendung eval()im Kontext seiner globalsund localskwargs, dh eval(expression, globals=None, locals=None)(siehe Dokumente für eval hier ).

Diese können verwendet werden, um die Funktionen einzuschränken, die über die evalFunktion verfügbar sind . Wenn Sie beispielsweise einen neuen Python-Interpreter laden, ist der locals()und globals()derselbe und sieht ungefähr so ​​aus:

>>>globals()
{'__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__doc__': None,
 '__spec__': None, '__builtins__': <module 'builtins' (built-in)>,
 '__package__': None, '__name__': '__main__'}

Es gibt sicherlich Funktionen innerhalb des builtinsModuls, die einem System erheblichen Schaden zufügen können. Aber es ist möglich, alles zu blockieren, was wir nicht zur Verfügung haben wollen. Nehmen wir ein Beispiel. Angenommen, wir möchten eine Liste erstellen, die eine Domäne der verfügbaren Kerne auf einem System darstellt. Für mich habe ich 8 Kerne, also würde ich eine Liste wollen [1, 8].

>>>from os import cpu_count
>>>eval('[1, cpu_count()]')
[1, 8]

Ebenso ist alles __builtins__verfügbar.

>>>eval('abs(-1)')
1

OK. Dort sehen wir also eine Funktion, die wir verfügbar machen möchten, und ein Beispiel für eine (von vielen, die viel komplexer sein können) Methode, die wir nicht verfügbar machen möchten. Also lasst uns alles blockieren.

>>>eval('[1, cpu_count()]', {'__builtins__':None}, {})
TypeError: 'NoneType' object is not subscriptable

Wir haben alle __builtins__Funktionen effektiv blockiert und damit ein gewisses Maß an Schutz in unser System gebracht. An diesem Punkt können wir beginnen, Funktionen wieder hinzuzufügen, die verfügbar gemacht werden sollen.

>>>from os import cpu_count
>>>exposed_methods = {'cpu_count': cpu_count}
>>>eval('cpu_count()', {'__builtins__':None}, exposed_methods)
8
>>>eval('abs(cpu_count())', {'__builtins__':None}, exposed_methods)
TypeError: 'NoneType' object is not subscriptable

Jetzt haben wir die cpu_countFunktion zur Verfügung, während wir immer noch alles blockieren, was wir nicht wollen. Meiner Meinung nach ist dies sehr mächtig und eindeutig aus dem Umfang der anderen Antworten, keine gemeinsame Implementierung. Es gibt zahlreiche Verwendungsmöglichkeiten für so etwas und solange es richtig gehandhabt wird, evalkann es meiner Meinung nach sicher zu einem guten Preis verwendet werden.

NB

Etwas anderes Cooles an diesen kwargsist, dass Sie anfangen können, Kurzschrift für Ihren Code zu verwenden. Angenommen, Sie verwenden eval als Teil einer Pipeline, um importierten Text auszuführen. Der Text muss keinen genauen Code enthalten, kann einem bestimmten Vorlagendateiformat folgen und dennoch alles ausführen, was Sie möchten. Zum Beispiel:

>>>from os import cpu_count
>>>eval('[1,cores]', {'__builtins__': None}, {'cores': cpu_count()})
[1, 8]

29

In Python 2.x input(...)ist gleichbedeutend mit eval(raw_input(...)), in Python 3.x raw_inputwurde umbenannt input, was meiner Meinung nach zu Ihrer Verwirrung führte (Sie haben sich wahrscheinlich die Dokumentation inputin Python 2.x angesehen). Darüber hinaus eval(input(...))würde in Python 3.x gut funktionieren , würde aber TypeErrorin Python 2 ein auslösen.

In diesem Fall evalwird verwendet, um die zurückgegebene Zeichenfolge inputin einen Ausdruck zu zwingen und zu interpretieren. Im Allgemeinen wird dies als schlechte Praxis angesehen.


Oder es ist ein Python 3.x-Buch, wo inputbedeutet, was raw_inputin 2.x getan hat.
Dan04

1
Ja, das ist mir eingefallen, nachdem ich meine erste Antwort geschrieben habe, und das ist eindeutig der Fall.
Zeekay

6

Vielleicht ein irreführendes Beispiel dafür, wie man eine Zeile liest und interpretiert.

Versuchen Sie eval(input())und tippen Sie "1+1"- dies sollte gedruckt werden 2. Eval wertet Ausdrücke aus.


Warum sollte ich es zwischen Anführungszeichen eingeben? Die Eingabe erhält eine Zeichenfolge und übergibt sie an eval, ohne den Code auszuführen. Es wäre also in Ordnung, wenn ich einfach 1 + 1 eingeben würde ... ¿?
JC Rocamonde

Die Sache ist, dass Sie P2.x und 3.x mischen. In Python 2 funktioniert Ihr Code, aber es ist nicht sinnvoll, zweimal auszuwerten. In Python 3 ist dies nicht der Fall und es wird eine Zeichenfolge zurückgegeben.
JC Rocamonde

6

eval()wertet die übergebene Zeichenfolge als Python-Ausdruck aus und gibt das Ergebnis zurück. Zum Beispiel eval("1 + 1")interpretiert und führt die Expression "1 + 1"und gibt das Ergebnis (2).

Ein Grund, warum Sie verwirrt sein könnten, ist, dass der von Ihnen zitierte Code eine Indirektionsebene beinhaltet. Der innere Funktionsaufruf (Eingabe) wird zuerst ausgeführt, damit der Benutzer die Eingabeaufforderung "blah" sieht. Stellen wir uns vor, sie antworten mit "1 + 1" (Anführungszeichen aus Gründen der Übersichtlichkeit, geben Sie sie beim Ausführen Ihres Programms nicht ein). Die Eingabefunktion gibt diese Zeichenfolge zurück, die dann an die äußere Funktion (eval) übergeben wird, die die Zeichenfolge und interpretiert gibt das Ergebnis zurück (2).

Lesen Sie mehr über eval hier .


6

eval()wertet, wie der Name schon sagt, das übergebene Argument aus.

raw_input()ist jetzt input()in Python 3.x-Versionen. Das am häufigsten vorkommende Beispiel für die Verwendung von eval()ist daher die Verwendung der Funktionalität, input()die in der 2.x-Version von Python bereitgestellt wird. raw_input gab die vom Benutzer eingegebenen Daten als Zeichenfolge zurück, während die Eingabe den Wert der eingegebenen Daten auswertete und zurückgab.

eval(input("bla bla"))Somit wird die Funktionalität von input()in 2.x repliziert , dh die vom Benutzer eingegebenen Daten auszuwerten.

Kurz gesagt: eval()wertet die an ihn übergebenen Argumente aus und gibt daher eval('1 + 1')2 zurück.


6

Eine nützliche Anwendung von eval()ist das Auswerten von Python-Ausdrücken aus Zeichenfolgen. Beispiel: Laden aus der Dateistringdarstellung des Wörterbuchs:

running_params = {"Greeting":"Hello "}
fout = open("params.dat",'w')
fout.write(repr(running_params))
fout.close()

Lesen Sie es als Variable vor und bearbeiten Sie es:

fin = open("params.dat",'r')
diction=eval(fin.read())
diction["Greeting"]+="world"
fin.close()
print diction

Ausgabe:

{'Greeting': 'Hello world'}

7
Wie beantwortet dies die Frage, was fragt eval?
JKD

4

Ich bin spät dran, um diese Frage zu beantworten, aber niemand scheint eine klare Antwort auf die Frage zu geben.

Wenn ein Benutzer einen numerischen Wert eingibt, input()wird eine Zeichenfolge zurückgegeben.

>>> input('Enter a number: ')
Enter a number: 3
>>> '3'
>>> input('Enter a number: ')
Enter a number: 1+1
'1+1'

So eval()wertet zurückgegebene Wert (oder Ausdruck) , die ein String und Rückkehr integer / float ist.

>>> eval(input('Enter a number: '))
Enter a number: 1+1
2
>>> 
>>> eval(input('Enter a number: '))
Enter a number: 3.14
3.14

Natürlich ist dies eine schlechte Praxis. int()oder float()sollte anstelle von eval()in diesem Fall verwendet werden.

>>> float(input('Enter a number: '))
Enter a number: 3.14
3.14

3

Eine andere Option, wenn Sie die Auswertungszeichenfolge auf einfache Literale beschränken möchten, ist die Verwendung ast.literal_eval(). Einige Beispiele:

import ast

# print(ast.literal_eval(''))          # SyntaxError: unexpected EOF while parsing
# print(ast.literal_eval('a'))         # ValueError: malformed node or string
# print(ast.literal_eval('import os')) # SyntaxError: invalid syntax
# print(ast.literal_eval('1+1'))       # 2: but only works due to a quirk in parser
# print(ast.literal_eval('1*1'))       # ValueError: malformed node or string
print(ast.literal_eval("{'a':1}"))     # {'a':1}

Aus den Dokumenten :

Bewerten Sie sicher einen Ausdrucksknoten oder eine Zeichenfolge, die ein Python-Literal oder eine Containeranzeige enthält. Die bereitgestellte Zeichenfolge oder der bereitgestellte Knoten darf nur aus den folgenden Python-Literalstrukturen bestehen: Zeichenfolgen, Bytes, Zahlen, Tupel, Listen, Diktate, Mengen, Boolesche Werte und Keine.

Dies kann zum sicheren Auswerten von Zeichenfolgen verwendet werden, die Python-Werte aus nicht vertrauenswürdigen Quellen enthalten, ohne dass die Werte selbst analysiert werden müssen. Es ist nicht in der Lage, beliebig komplexe Ausdrücke auszuwerten, z. B. Operatoren oder Indizierung.

Warum es so begrenzt ist, aus der Mailingliste :

Das Zulassen von Operatorausdrücken mit Literalen ist möglich, aber viel komplexer als die aktuelle Implementierung. Eine einfache Implementierung ist nicht sicher: Sie können ohne Aufwand eine grundsätzlich unbegrenzte CPU- und Speichernutzung induzieren (versuchen Sie "9 ** 9 ** 9" oder "[Keine] * 9 ** 9").

Was die Nützlichkeit betrifft, ist diese Funktion nützlich, um Literalwerte und Container, wie durch repr () gekennzeichnet, "zurückzulesen". Dies kann beispielsweise für die Serialisierung in einem Format verwendet werden, das JSON ähnelt, aber leistungsfähiger ist.


1
ast.literal_evalunterstützt entgegen Ihrem '1+1'Beispiel keine Operatoren . Trotzdem unterstützt es Listen, Zahlen, Zeichenfolgen usw. und ist daher eine gute Alternative für gängige evalAnwendungsfälle.
Benjamin

@benjimin oh du hast recht - es ist nur eine Eigenart, dass es 1 + 1 akzeptiert! stackoverflow.com/questions/40584417/…
Brian Burns
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.