Cosa fa Python's eval ()?


306

Nel libro che sto leggendo su Python, continua a usare il codice eval(input('blah'))

Ho letto la documentazione e la capisco, ma non vedo ancora come cambia la input()funzione.

Che cosa fa? Qualcuno può spiegare?


4
La funzione Eval tenta di eseguire e interpretare la stringa (argomento) passata ad essa come codice Python. x = 1 print (eval ('x + 1')) L'output del codice sopra sarà 2. Lo svantaggio di tale approccio è che l'utente ottiene l'indipendenza dalla scrittura del codice che può causare condizioni di caos. Sebbene tu possa limitare gli utenti da accedere a molte variabili e metodi passando parametri globali e locali in funzione di valutazione.
ATIF IBAD KHAN,

Risposte:


276

La funzione eval consente a un programma Python di eseguire il codice Python al suo interno.

esempio di eval (shell interattiva):

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

25
ahah, quello era un esempio banale, ma potevi permettere all'utente di digitare un comando arbitrario e far eseguire a Python. Quindi potresti avere il tipo utente in una stringa di comando e quindi far eseguire a Python come codice. Ad esempio: eval ("__ import __ ('os'). Remove ('file')").
BYS2,

60
Sembrerà inutile finché non ne avrai bisogno. Viene utilizzato su siti come codepad.org per consentire l'esecuzione di script in un ambiente di test. eval()può anche essere utilizzato per eseguire codice altamente dinamico, ma è necessario rendersi pienamente consapevoli dei rischi di sicurezza e prestazioni prima di utilizzarlo.
George Cummins,

6
@GeorgeCummins, codepad.org non usa eval, né potrebbe fare quello che fa eval.
Mike Graham,

16
@GeorgeCummins: codepag.org esegue tutto in una sandbox: una prigione chroot con controlli ptrace in una macchina virtuale per impedire a codice dannoso di fare qualcosa di male. Molto più complicato di un semplice giudizio. Inoltre, eval è specifico di Python. codepad supporta un sacco di lingue.
FogleBird,

4
@GeorgeCummins, codepad esegue un sistema molto complesso per eseguire in modo sicuro programmi arbitrari. eval, oltre ad essere insicuro, non può eseguire interi programmi come fa Codepad perché può solo valutare una singola espressione.
Mike Graham,

165

eval()interpreta una stringa come codice. Il motivo per cui così tante persone ti hanno avvertito dell'utilizzo di questo è perché un utente può utilizzarlo come opzione per eseguire il codice sul computer. Se hai eval(input())e osimportato, una persona potrebbe digitare in input() os.system('rm -R *')cui eliminare tutti i tuoi file nella tua home directory. (Supponendo di avere un sistema unix). L'uso eval()è un buco nella sicurezza. Se hai bisogno di convertire stringhe in altri formati, prova a usare cose che lo fanno, ad esempio int().


14
Vuoi dire che usare evalcon input()è un buco nella sicurezza. Non mettere input()dentro una dichiarazione di valutazione e starai bene.
Rohmer,

19
@Rohmer, i dati non sicuri possono provenire da qualsiasi luogo: richieste Web, campi di input modulo, letture di file, ... non solo dall'input della console. Anche se scrivi i file da solo, può comunque contenere input originariamente proveniente da una fonte non attendibile. Quindi evalè un problema di sicurezza in molti casi.
sanderd17,

3
poiché di inputsolito prende i suoi dati dalla console l'utente potrebbe semplicemente uscire dal programma e digitare rm -R *comunque ...
cz

63

Molte buone risposte qui, ma nessuna descrive l'uso eval()nel contesto del suo globalse localskwargs, cioè eval(expression, globals=None, locals=None)(vedi i documenti per eval qui ).

Questi possono essere utilizzati per limitare le funzioni disponibili tramite la evalfunzione. Ad esempio, se carichi un nuovo interprete Python locals()e globals()sarà lo stesso e assomiglierà a questo:

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

Ci sono certamente funzioni all'interno del builtinsmodulo che possono causare danni significativi a un sistema. Ma è possibile bloccare qualsiasi cosa e tutto ciò che non vogliamo disponibile. Facciamo un esempio. Supponiamo di voler costruire un elenco per rappresentare un dominio dei core disponibili su un sistema. Per me ho 8 core, quindi vorrei un elenco [1, 8].

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

Allo stesso modo tutto __builtins__è disponibile.

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

Ok. Quindi vediamo una funzione che vogliamo esporre e un esempio di un metodo (di molti che può essere molto più complesso) che non vogliamo esporre. Quindi blocciamo tutto.

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

Abbiamo effettivamente bloccato tutte le __builtins__funzioni e come tale introdotto un livello di protezione nel nostro sistema. A questo punto possiamo iniziare ad aggiungere nuovamente funzioni che vogliamo esporre.

>>>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

Ora abbiamo la cpu_countfunzione disponibile pur bloccando tutto ciò che non vogliamo. Secondo me, questo è super potente e chiaramente dal campo di applicazione delle altre risposte, non un'implementazione comune. Ci sono numerosi usi per qualcosa di simile e fintanto che è gestito correttamente, personalmente ritengo che evalpossa essere utilizzato in modo sicuro e di grande valore.

NB

Un'altra cosa interessante di questi kwargsè che puoi iniziare a usare la scorciatoia per il tuo codice. Diciamo che usi eval come parte di una pipeline per eseguire del testo importato. Il testo non deve avere un codice esatto, può seguire un formato di file modello ed eseguire comunque tutto ciò che desideri. Per esempio:

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

29

In Python 2.x input(...)è equivalente a eval(raw_input(...)), in Python 3.x è raw_inputstato rinominato input, che sospetto abbia portato alla tua confusione (probabilmente stavi guardando la documentazione di inputPython 2.x). Inoltre, eval(input(...))funzionerebbe bene in Python 3.x, ma aumenterebbe a TypeErrorin Python 2.

In questo caso evalviene utilizzato per forzare la stringa restituita da inputin un'espressione e interpretata. Generalmente questa è considerata una cattiva pratica.


Oppure è un libro di Python 3.x, dove inputsignifica cosa ha raw_inputfatto in 2.x.
dan04,

1
Sì, mi è venuto in mente dopo aver scritto la mia risposta iniziale, e questo è chiaramente il caso.
Zeekay,

6

Forse un esempio fuorviante di leggere una riga e interpretarla.

Prova eval(input())e digita "1+1"- questo dovrebbe stampare 2. Eval valuta le espressioni.


Perché dovrei scriverlo tra virgolette? L'input sta ottenendo una stringa e la passa a eval, non esegue il codice, quindi dovrei andare bene se avessi semplicemente digitato 1 + 1 ... ¿?
JC Rocamonde,

Il fatto è che stai mescolando P2.xe 3.x. In Python 2 il tuo codice funziona, ma non ha senso valutare due volte. In python 3 non funziona e restituisce una stringa.
JC Rocamonde,

6

eval()valuta la stringa passata come espressione Python e restituisce il risultato. Ad esempio, eval("1 + 1")interpreta ed esegue l'espressione "1 + 1"e restituisce il risultato (2).

Uno dei motivi per cui potresti essere confuso è perché il codice che hai citato comporta un livello di riferimento indiretto. La chiamata di funzione interna (input) viene eseguita per prima, quindi l'utente vede il prompt "blah". Immaginiamo che rispondano con "1 + 1" (virgolette aggiunte per chiarezza, non digitarle durante l'esecuzione del programma), la funzione di input restituisce quella stringa, che viene quindi passata alla funzione esterna (eval) che interpreta la stringa e restituisce il risultato (2).

Leggi di più su eval qui .


6

eval(), come suggerisce il nome, valuta l'argomento passato.

raw_input()è ora input()nelle versioni 3.x di Python. Quindi l'esempio più comunemente trovato per l'uso di eval()è il suo uso per fornire la funzionalità input()fornita nella versione 2.x di python. raw_input ha restituito i dati immessi dall'utente come stringa, mentre l'input ha valutato il valore dei dati immessi e li ha restituiti.

eval(input("bla bla"))replica quindi la funzionalità di input()in 2.x, ovvero la valutazione dei dati inseriti dall'utente.

In breve: eval()valuta gli argomenti passati ad esso e quindi eval('1 + 1')restituito 2.


6

Una delle applicazioni utili di eval() è valutare le espressioni Python dalla stringa. Ad esempio caricare dalla rappresentazione della stringa di file del dizionario:

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

Leggilo come variabile e modificalo:

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

Produzione:

{'Greeting': 'Hello world'}

7
Come risponde alla domanda che pone cosa evalfa?
jkd,

4

Sono in ritardo per rispondere a questa domanda, ma nessuno sembra dare una risposta chiara alla domanda.

Se un utente immette un valore numerico, input()restituirà una stringa.

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

Quindi, eval()valuterà il valore restituito (o espressione) che è una stringa e restituirà intero / float.

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

Di certo questa è una cattiva pratica. int()o float()dovrebbe essere usato invece che eval()in questo caso.

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

3

Un'altra opzione se si desidera limitare la stringa di valutazione a letterali semplici è quella di utilizzare ast.literal_eval(). Qualche esempio:

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}

Dai documenti :

Valuta in modo sicuro un nodo di espressione o una stringa contenente un display letterale o contenitore Python. La stringa o il nodo forniti possono solo costituiti dalle seguenti strutture letterali Python: stringhe, byte, numeri, tuple, elenchi, dadi, set, valori booleani e Nessuno.

Questo può essere usato per valutare in sicurezza stringhe contenenti valori Python da fonti non attendibili senza la necessità di analizzare i valori da soli. È non in grado di valutare espressioni arbitrariamente complesse, ad esempio coinvolgendo operatori o indicizzazione.

Per quanto riguarda il motivo per cui è così limitato, dalla mailing list :

Consentire espressioni operatore con valori letterali è possibile, ma molto più complesso dell'attuale implementazione. Una semplice implementazione non è sicura: puoi indurre un utilizzo sostanzialmente illimitato di CPU e memoria senza sforzo (prova "9 ** 9 ** 9" o "[Nessuno] * 9 ** 9").

Per quanto riguarda l'utilità, questa funzione è utile per "rileggere" valori e contenitori letterali come indicato da repr (). Questo può ad esempio essere usato per la serializzazione in un formato simile ma più potente di JSON.


1
ast.literal_evalnon supporta gli operatori, contrariamente al tuo '1+1'esempio. Ciononostante supporta elenchi, numeri, stringhe ecc. Ed è quindi una buona alternativa ai evalcasi d'uso più comuni .
benjimin,

@benjimin oh hai ragione - è solo una stranezza che accetta 1 + 1! stackoverflow.com/questions/40584417/…
Brian Burns,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.