Que fait eval () de Python?

306

Dans le livre que je lis sur Python, il continue d'utiliser le code eval(input('blah'))

J'ai lu la documentation et je la comprends, mais je ne vois toujours pas comment cela change la input()fonction.

Qu'est ce que ça fait? Quelqu'un peut-il expliquer?

Billjk
la source
4
La fonction Eval essaie d'exécuter et d'interpréter la chaîne (argument) qui lui est transmise en tant que code python. x = 1 print (eval ('x + 1')) La sortie du code ci-dessus sera 2. L'inconvénient d'une telle approche est que, l'utilisateur obtient l'indépendance de l'écriture de code qui peut entraîner des conditions de ravage. Bien que vous puissiez restreindre les utilisateurs de accéder à de nombreuses variables et méthodes en passant des paramètres globaux et locaux dans la fonction eval.
ATIF IBAD KHAN

Réponses:

276

La fonction eval permet à un programme Python d'exécuter du code Python en lui-même.

exemple eval (shell interactif):

>>> x = 1
>>> eval('x + 1')
2
>>> eval('x')
1
BYS2
la source
25
haha, c'était un exemple trivial, mais vous pouvez laisser l'utilisateur taper une commande arbitraire et demander à python de l'exécuter. Vous pouvez donc demander à l'utilisateur de taper une chaîne de commande, puis de l'exécuter en tant que code. Par exemple: eval ("__ import __ ('os'). Remove ('file')").
BYS2
60
Cela vous semblera inutile jusqu'à ce que vous en trouviez le besoin. Il est utilisé sur des sites comme codepad.org pour vous permettre d'exécuter des scripts dans un environnement de test. eval()peut également être utilisé pour exécuter du code hautement dynamique, mais vous devez vous rendre pleinement conscient des risques de sécurité et de performances avant de l'utiliser.
George Cummins
6
@GeorgeCummins, codepad.org n'utilise pas eval, ni ne pourrait faire ce qu'il fait avec eval.
Mike Graham
16
@GeorgeCummins: codepag.org exécute tout dans un bac à sable: une prison chroot avec ptrace vérifie dans une machine virtuelle pour empêcher le code malveillant de faire quoi que ce soit de mal. Beaucoup plus compliqué qu'un simple eval. De plus, eval est spécifique à Python. codepad prend en charge un tas de langues.
FogleBird
4
@GeorgeCummins, codepad exécute un système très complexe pour exécuter en toute sécurité des programmes arbitraires. eval, autre que l'insécurité, ne peut pas exécuter des programmes entiers comme le fait le codepad car il ne peut évaluer qu'une seule expression.
Mike Graham
165

eval()interprète une chaîne comme du code. La raison pour laquelle tant de personnes vous ont mis en garde contre l'utilisation de cette fonctionnalité est qu'un utilisateur peut l'utiliser comme option pour exécuter du code sur l'ordinateur. Si vous l'avez eval(input())et osimporté, une personne pourrait taper dans input() os.system('rm -R *')lequel supprimer tous vos fichiers dans votre répertoire personnel. (En supposant que vous ayez un système Unix). L'utilisation eval()est un trou de sécurité. Si vous devez convertir des chaînes dans d'autres formats, essayez d'utiliser des choses qui font cela, comme int().

CoffeeRain
la source
14
Vous voulez dire que l'utilisation evalavec input()est une faille de sécurité. Ne mettez pas input()dans une déclaration eval et tout ira bien.
Rohmer
19
@Rohmer, les données dangereuses peuvent provenir de partout: requêtes Web, champs de saisie de formulaire, lectures de fichiers, ... pas seulement à partir de l'entrée de la console. Même si vous écrivez les fichiers vous-même, ils peuvent toujours contenir des entrées qui provenaient à l'origine d'une source non fiable. Il en evalva de même pour la sécurité dans de nombreux cas.
sanderd17
3
car il inputprend généralement ses données depuis la console, l'utilisateur peut simplement quitter le programme et taper de rm -R *toute façon ...
cz
63

Beaucoup de bonnes réponses ici, mais aucune ne décrit l'utilisation de eval()dans le contexte de its globalset localskwargs, c'est-à-dire eval(expression, globals=None, locals=None)(voir la documentation eval ici ).

Ceux-ci peuvent être utilisés pour limiter les fonctions disponibles via la evalfonction. Par exemple, si vous chargez un nouvel interpréteur python, le locals()et globals()sera le même et ressemblera à ceci:

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

Il existe certainement des fonctions au sein du builtinsmodule qui peuvent endommager considérablement un système. Mais il est possible de bloquer tout et tout ce dont nous ne voulons pas qu'il soit disponible. Prenons un exemple. Disons que nous voulons construire une liste pour représenter un domaine des cœurs disponibles sur un système. Pour moi, j'ai 8 cœurs, je voudrais donc une liste [1, 8].

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

De même, tout __builtins__est disponible.

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

D'accord. Nous voyons donc là une fonction que nous voulons exposer et un exemple d'une méthode (parmi tant d'autres qui peuvent être beaucoup plus complexes) que nous ne voulons pas exposer. Alors bloquons tout.

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

Nous avons effectivement bloqué toutes les __builtins__fonctions et, à ce titre, apporté un niveau de protection à notre système. À ce stade, nous pouvons commencer à ajouter des fonctions que nous voulons exposer.

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

Maintenant, nous avons la cpu_countfonction disponible tout en bloquant tout ce que nous ne voulons pas. À mon avis, c'est super puissant et clairement de la portée des autres réponses, pas une mise en œuvre commune. Il y a de nombreuses utilisations pour quelque chose comme ça et tant qu'il est manipulé correctement, je pense personnellement qu'il evalpeut être utilisé en toute sécurité à grande valeur.

NB

Une autre chose intéressante à ce sujet kwargsest que vous pouvez commencer à utiliser des raccourcis pour votre code. Supposons que vous utilisez eval dans le cadre d'un pipeline pour exécuter du texte importé. Le texte n'a pas besoin d'avoir un code exact, il peut suivre un format de fichier modèle et toujours exécuter tout ce que vous souhaitez. Par exemple:

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

En Python 2.x input(...)est équivalent à eval(raw_input(...)), en Python 3.x a raw_inputété renommé input, ce qui, je le soupçonne, a semé la confusion (vous regardiez probablement la documentation de inputPython 2.x). De plus, eval(input(...))cela fonctionnerait bien dans Python 3.x, mais déclencherait un TypeErrordans Python 2.

Dans ce cas, evalest utilisé pour contraindre la chaîne renvoyée de inputdans une expression et interprétée. En général, cela est considéré comme une mauvaise pratique.

Zeekay
la source
Ou c'est un livre Python 3.x, où inputsignifie ce qui a raw_inputfait dans 2.x.
dan04
1
Oui, cela m'est venu à l'esprit après avoir écrit ma réponse initiale, et c'est clairement le cas.
zeekay
6

Peut-être un exemple trompeur de lecture d'une ligne et d'interprétation.

Essayez de eval(input())taper "1+1"- cela devrait imprimer 2. Eval évalue les expressions.

hburde
la source
Pourquoi devrais-je le taper entre guillemets? L'entrée obtient une chaîne et la transmet à eval, n'exécutant pas le code, donc je serais bien si je tapais simplement 1 + 1 ... ¿?
JC Rocamonde du
Le fait est que vous mélangez P2.x et 3.x. En Python 2, votre code fonctionne, mais cela n'a pas de sens d'être évalué deux fois. En python 3, ce n'est pas le cas et renvoie une chaîne.
JC Rocamonde
6

eval()évalue la chaîne passée en tant qu'expression Python et renvoie le résultat. Par exemple, eval("1 + 1")interprète et exécute l'expression "1 + 1"et renvoie le résultat (2).

Une raison pour laquelle vous pourriez être confus est que le code que vous avez cité implique un niveau d'indirection. L'appel de fonction interne (entrée) est exécuté en premier afin que l'utilisateur voit l'invite "bla". Imaginons qu'ils répondent avec "1 + 1" (guillemets ajoutés pour plus de clarté, ne les tapez pas lors de l'exécution de votre programme), la fonction d'entrée renvoie cette chaîne, qui est ensuite transmise à la fonction externe (eval) qui interprète la chaîne et renvoie le résultat (2).

En savoir plus sur eval ici .

Marc Cohen
la source
6

eval(), comme son nom l'indique, évalue l'argument passé.

raw_input()est maintenant input()dans les versions python 3.x. Ainsi, l'exemple d'utilisation le plus courant eval()est son utilisation pour fournir les fonctionnalités input()fournies dans la version 2.x de python. raw_input a renvoyé les données entrées par l'utilisateur sous forme de chaîne, tandis que l'entrée a évalué la valeur des données entrées et l'a renvoyée.

eval(input("bla bla"))reproduit ainsi la fonctionnalité de input()dans 2.x, c'est-à-dire d'évaluer les données saisies par l'utilisateur.

En bref: eval()évalue les arguments qui lui sont passés et donc eval('1 + 1')retournent 2.

Rubal
la source
6

L'une des applications utiles de eval()est d'évaluer les expressions python à partir d'une chaîne. Par exemple, charge à partir d'une représentation sous forme de chaîne de fichier du dictionnaire:

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

Lisez-le en tant que variable et modifiez-le:

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

Production:

{'Greeting': 'Hello world'}
Nikolay Frick
la source
7
Comment cela répond-il à la question qui demande quoi eval?
jkd
4

Je suis en retard pour répondre à cette question, mais personne ne semble répondre clairement à la question.

Si un utilisateur entre une valeur numérique, input()retournera une chaîne.

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

Donc, eval()va évaluer la valeur retournée (ou l'expression) qui est une chaîne et retourner un entier / flottant.

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

Bien sûr, c'est une mauvaise pratique. int()ou float()doit être utilisé à la place de eval()dans ce cas.

>>> float(input('Enter a number: '))
Enter a number: 3.14
3.14
Calvin Kim
la source
3

Une autre option si vous souhaitez limiter la chaîne d'évaluation à des littéraux simples est d'utiliser ast.literal_eval(). Quelques exemples:

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}

De la documentation :

Évaluez en toute sécurité un nœud d'expression ou une chaîne contenant un littéral Python ou un affichage de conteneur. La chaîne ou le nœud fourni ne peut être composé que des structures littérales Python suivantes: chaînes, octets, nombres, tuples, listes, dict, ensembles, booléens et Aucun.

Cela peut être utilisé pour évaluer en toute sécurité des chaînes contenant des valeurs Python à partir de sources non fiables sans avoir besoin d'analyser les valeurs soi-même. Il n'est pas capable d'évaluer des expressions arbitrairement complexes, par exemple impliquant des opérateurs ou une indexation.

Quant à savoir pourquoi c'est si limité, à partir de la liste de diffusion :

Autoriser des expressions d'opérateur avec des littéraux est possible, mais beaucoup plus complexe que l'implémentation actuelle. Une implémentation simple n'est pas sûre: vous pouvez induire une utilisation du processeur et de la mémoire pratiquement illimitée sans effort (essayez "9 ** 9 ** 9" ou "[None] * 9 ** 9").

Quant à l'utilité, cette fonction est utile pour «relire» les valeurs littérales et les conteneurs tels que stringifiés par repr (). Cela peut par exemple être utilisé pour la sérialisation dans un format similaire mais plus puissant que JSON.

Brian Burns
la source
1
ast.literal_evalne prend pas en charge les opérateurs, contrairement à votre '1+1'exemple. Néanmoins, il prend en charge les listes, les nombres, les chaînes, etc., et constitue donc une bonne alternative pour les evalcas d'utilisation courants .
benjimin
@benjimin oh vous avez raison - c'est juste une bizarrerie qu'il accepte 1 + 1! stackoverflow.com/questions/40584417/…
Brian Burns