web-dev-qa-db-fra.com

Que fait Python's eval ()?

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

J'ai lu la documentation et je l'ai comprise, mais je ne vois toujours pas en quoi cela change la fonction input().

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

262
Billjk

La fonction eval laisse un programme Python exécuter Python code en lui-même.

exemple eval (shell interactif):

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

eval() interprète une chaîne en tant que code. La raison pour laquelle tant de gens vous ont averti de l’utiliser est qu’un utilisateur peut l’utiliser comme une option pour exécuter du code sur l’ordinateur. Si vous avez eval(input()) et os, une personne peut taper input()os.system('rm -R *') pour supprimer tous vos fichiers de votre répertoire personnel. (En supposant que vous ayez un système unix). Utiliser eval() est une faille de sécurité. Si vous avez besoin de convertir des chaînes de caractères en d'autres formats, essayez d'utiliser des méthodes similaires, telles que int().

143
CoffeeRain

Beaucoup de bonnes réponses ici, mais aucune ne décrit l'utilisation de eval() dans le contexte de ses globals et locals kwargs, c'est-à-dire eval(expression, globals=None, locals=None) (voir docs pour evalici ).

Celles-ci peuvent être utilisées pour limiter les méthodes disponibles via la méthode eval. Par exemple, si vous chargez un nouvel interprète python, les fonctions locals() et globals() seront identiques et ressembleront à ceci:

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

Il existe certainement des méthodes dans le module builtins qui peuvent causer des dommages importants à un système. Mais il est possible de bloquer tout ce que nous ne voulons pas voir disponible. Prenons un exemple. Supposons que nous voulions construire une liste pour représenter un domaine des cœurs disponibles sur un système. Pour moi, j'ai 8 cœurs, donc je voudrais 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 une méthode à exposer et un exemple d’une méthode (parmi d’autres qui peut être beaucoup plus complexe) que nous ne voulons pas exposer. Alors laisse tout bloquer.

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

Nous avons effectivement bloqué toutes les méthodes __builtins__ et avons ainsi apporté un niveau de protection à notre système. À ce stade, nous pouvons commencer à ajouter des méthodes 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

Nous avons maintenant la méthode cpu_count disponible tout en bloquant tout ce que nous ne voulons pas. À mon avis, cela est super puissant et clairement du champ des autres réponses, pas une implémentation commune. Il existe de nombreuses utilisations pour quelque chose comme cela et tant que cela est géré correctement, j’ai le sentiment que eval peut être utilisé en toute sécurité à bon escient.

N.B.

Une autre chose intéressante avec ces kwargs est que vous pouvez commencer à utiliser un raccourci pour votre code. Supposons que vous utilisiez eval dans le cadre d'un pipeline pour exécuter du texte importé. Le texte n'a pas besoin de code exact, il peut suivre un format de fichier modèle et exécuter tout ce que vous voulez. Par exemple:

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

Dans Python 2.x input(...) est équivalent à eval(raw_input(...)), dans Python 3.x raw_input a été renommé input, lequel Je suppose que cela a entraîné votre confusion (vous avez probablement consulté la documentation de input dans Python 2.x). De plus, eval(input(...)) fonctionnerait bien dans Python 3.x, mais déclencherait une TypeError dans Python 2.

Dans ce cas, eval est utilisé pour contraindre la chaîne renvoyée par input dans une expression et interprétée. Généralement, cela est considéré comme une mauvaise pratique.

28
zeekay

eval() évalue la chaîne transmise 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 des raisons pour lesquelles vous pourriez être confus est que le code que vous avez cité implique un niveau d'indirection. L’appel de la fonction interne (entrée) est exécuté en premier afin que l’utilisateur voie l’invite "blah". 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 here .

6
Marc Cohen

Une des applications utiles de eval() consiste à évaluer les expressions python de chaîne. Par exemple, chargez à partir de la représentation de la 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 éditez-le:

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

Sortie:

{'Greeting': 'Hello world'}
5
Nikolay Frick

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

raw_input() est maintenant input() dans python versions 3.x. Ainsi, l'exemple le plus commun pour l'utilisation de eval() est son utilisation pour fournir la fonctionnalité fournie par input() 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 donc la fonctionnalité de input() en 2.x, c'est-à-dire, l'évaluation des données saisies par l'utilisateur.

En bref: eval() évalue les arguments qui lui ont été transmis et donc eval('1 + 1') a renvoyé 2.

5
Rubal

Peut-être un exemple trompeur de lire une ligne et de l'interpréter.

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

5
hburde

Je suis en retard pour répondre à cette question, mais personne ne semble donner une réponse claire à la question.

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

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

Ainsi, eval() évaluera la valeur renvoyée (ou expression) qui est une chaîne et renverra un entier/float.

>>> 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
4
Calvin Kim

Une autre option si vous souhaitez limiter la chaîne d'évaluation à de simples littéraux consiste à 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 docs :

Évaluez en toute sécurité un nœud d'expression ou une chaîne contenant un Python littéral ou un affichage de conteneur. La chaîne ou le nœud fourni peut uniquement comprendre les structures littérales Python suivantes: chaînes, octets, nombres, n-uplets, listes, dict , sets, booleans et None.

Ceci peut être utilisé pour évaluer en toute sécurité des chaînes contenant Python valeurs provenant de sources non fiables sans avoir à analyser elles-mêmes les valeurs. 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é, 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 pratiquement illimitée du processeur et de la mémoire (essayez "9 ** 9 ** 9" ou "[Aucun] * 9 ** 9").

En ce qui concerne l’utilité, cette fonction est utile pour "relire" les valeurs littérales et les conteneurs tels qu’ils sont stringifiés par repr (). Cela peut par exemple être utilisé pour la sérialisation dans un format similaire mais plus puissant que JSON.

2
Brian Burns