Что делает eval () в Python?

306

В книге, которую я читаю на Python, он продолжает использовать код eval(input('blah'))

Я прочитал документацию, и я понимаю это, но я все еще не вижу, как это меняет input()функцию.

Что оно делает? Может кто-нибудь объяснить?

Billjk
источник
4
Функция Eval пытается выполнить и интерпретировать строку (аргумент), переданную ей, как код Python. x = 1 print (eval ('x + 1')) Вывод вышеупомянутого кода будет 2. Недостаток такого подхода заключается в том, что пользователь получает независимость от написания кода, что может привести к хаосу. Хотя вы можете ограничить пользователей от доступ ко многим переменным и методам путем передачи глобального и локального параметра в функцию eval.
АТИФ ИБАД ХАН

Ответы:

276

Функция eval позволяет программе Python запускать код Python внутри себя.

Пример eval (интерактивная оболочка):

>>> x = 1
>>> eval('x + 1')
2
>>> eval('x')
1
BYS2
источник
25
ха-ха, это был тривиальный пример, но вы могли позволить пользователю ввести произвольную команду и заставить ее выполнить python. Таким образом, вы можете указать тип пользователя в командной строке, а затем Python запустить его как код. Например, eval ("__ import __ ('os'). Remove ('file')").
BYS2
61
Это покажется бесполезным, пока вы не найдете в этом необходимость. Он используется на сайтах, таких как codepad.org, чтобы позволить вам выполнять скрипты в тестовой среде. eval()также может использоваться для выполнения высокодинамичного кода, но вы должны полностью осознавать риски безопасности и производительности перед его использованием.
Джордж Камминс
6
@GeorgeCummins, codepad.org не использует evalи не может делать то, что делает eval.
Майк Грэм
16
@GeorgeCummins: codepag.org запускает все в «песочнице»: chroot-тюрьму с проверками ptrace на виртуальной машине, чтобы вредоносный код не делал ничего плохого. Гораздо сложнее, чем простой eval. Кроме того, eval специфичен для Python. Кодовая панель поддерживает несколько языков.
FogleBird
4
@GeorgeCummins, codepad использует очень сложную систему для безопасного запуска произвольных программ. evalкроме того, что он небезопасен, он не может запускать целые программы, как это делает codepad, потому что он может вычислять только одно выражение.
Майк Грэм
165

eval()интерпретирует строку как код Причина, по которой так много людей предупредили вас об этом, заключается в том, что пользователь может использовать это в качестве опции для запуска кода на компьютере. Если у вас есть eval(input())и osимпортированы, человек может ввести, input() os.system('rm -R *')который удалит все ваши файлы в вашем домашнем каталоге. (Предполагая, что у вас есть система Unix). Использование eval()это дыра в безопасности. Если вам нужно преобразовать строки в другие форматы, попробуйте использовать такие вещи, как int().

CoffeeRain
источник
14
Вы имеете в виду, что использование evalс input()является дырой в безопасности. Не помещайте input()в утверждение eval, и все будет в порядке.
Rohmer
19
@ Ромер, небезопасные данные могут поступать откуда угодно: веб-запросы, поля ввода формы, чтение файла ... и не только с консоли. Даже если вы пишете файлы самостоятельно, они все равно могут содержать входные данные, которые исходили из ненадежного источника. Так evalже как и проблема безопасности во многих случаях.
sanderd17
3
так inputкак обычно получает данные из консоли, пользователь может просто выйти из программы и в rm -R *любом случае набрать ...
cz
63

Здесь много хороших ответов, но ни один не описывает их использование eval()в контексте своих globalsи localskwargs, т. Е. eval(expression, globals=None, locals=None)(См. Документацию eval здесь ).

Их можно использовать для ограничения функций, доступных через evalфункцию. Например, если вы загрузите новый интерпретатор Python, то locals()и globals()будет то же самое и будет выглядеть примерно так:

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

В builtinsмодуле, безусловно, есть функции, которые могут нанести значительный ущерб системе. Но есть возможность заблокировать все, что мы не хотим. Давайте возьмем пример. Допустим, мы хотим создать список, представляющий домен доступных ядер в системе. Для меня у меня 8 ядер, поэтому я бы хотел список [1, 8].

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

Точно так же все __builtins__доступно.

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

Хорошо. Таким образом, мы видим одну функцию, которую мы хотим представить, и пример одного (из многих, который может быть гораздо более сложного) метода, который мы не хотим раскрывать. Итак, давайте заблокируем все.

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

Мы эффективно заблокировали все __builtins__функции и, таким образом, обеспечили уровень защиты нашей системы. На этом этапе мы можем начать добавлять обратно функции, которые мы хотим раскрыть.

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

Теперь у нас есть cpu_countдоступная функция, которая блокирует все, что мы не хотим. На мой взгляд, это супер мощный и явно из сферы других ответов, а не общей реализации. Существует множество вариантов использования чего-то подобного, и, при условии, что оно обрабатывается правильно, лично я чувствую, что оно evalможет быть безопасно использовано с высокой ценностью.

NB

Еще одна интересная вещь в kwargsтом, что вы можете начать использовать сокращение для своего кода. Допустим, вы используете eval как часть конвейера для выполнения некоторого импортированного текста. Текст не обязательно должен содержать точный код, он может соответствовать какому-либо формату файла шаблона и при этом выполнять все, что вы захотите. Например:

>>>from os import cpu_count
>>>eval('[1,cores]', {'__builtins__': None}, {'cores': cpu_count()})
[1, 8]
Grr
источник
29

В Python 2.x input(...)эквивалентно тому eval(raw_input(...)), что в Python 3.x raw_inputбыло переименовано input, что, как я подозреваю, привело к вашей путанице (вы, вероятно, просматривали документацию для inputPython 2.x). Кроме того, eval(input(...))будет хорошо работать в Python 3.x, но вызовет TypeErrorв Python 2.

В этом случае evalиспользуется для приведения строки, возвращаемой из inputв выражение и интерпретируемой. Обычно это считается плохой практикой.

zeekay
источник
Или это книга по Python 3.x, где inputозначает, что raw_inputсделал в 2.x.
Ден04
1
Да, это произошло со мной после того, как я написал свой первоначальный ответ, и это явно так.
Zeekay
6

Возможно, вводящий в заблуждение пример чтения строки и ее интерпретации.

Попробуйте eval(input())и введите "1+1"- это должно напечатать 2. Eval оценивает выражения.

hburde
источник
Почему я должен печатать его между кавычками? Ввод получает строку и передает ее в eval, не выполняя код, так что все будет в порядке, если я просто наберу 1 + 1 ... ¿?
JC Rocamonde
Дело в том, что вы смешиваете P2.x и 3.x. В Python 2 ваш код работает, но нет смысла оценивать его дважды. В Python 3 это не так, и возвращает строку.
JC Rocamonde
6

eval()оценивает переданную строку как выражение Python и возвращает результат. Например, eval("1 + 1")интерпретирует и выполняет выражение "1 + 1"и возвращает результат (2).

Одна из причин, по которой вы можете быть сбиты с толку, заключается в том, что приведенный вами код включает в себя определенный уровень косвенности. Внутренний вызов функции (input) выполняется первым, поэтому пользователь видит подсказку «blah». Давайте представим, что они отвечают «1 + 1» (кавычки добавлены для ясности, не вводите их при запуске вашей программы), функция ввода возвращает эту строку, которая затем передается внешней функции (eval), которая интерпретирует строку и возвращает результат (2).

Узнайте больше о Eval здесь .

Марк Коэн
источник
6

eval(), как следует из названия, оценивает переданный аргумент.

raw_input()сейчас input()в версиях Python 3.x. Таким образом, наиболее часто встречающимся примером использования eval()является его использование для обеспечения функциональности, input()предоставляемой в версии 2.x Python. raw_input возвращает введенные пользователем данные в виде строки, а input оценивает значение введенных данных и возвращает их.

eval(input("bla bla"))таким образом, копирует функциональность input()в 2.x, т. е. оценки введенных пользователем данных.

Вкратце: eval()оценивает переданные ему аргументы и, следовательно, eval('1 + 1')возвращает 2.

Rubal
источник
6

Одним из полезных применений eval()является оценка выражений Python из строки. Например загрузить из файла строковое представление словаря:

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

Прочитайте это как переменную и отредактируйте это:

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

Вывод:

{'Greeting': 'Hello world'}
Николай Фрик
источник
7
Как это отвечает на вопрос, который спрашивает, что evalделает?
JKD
4

Я опоздал, чтобы ответить на этот вопрос, но, кажется, никто не дает четкого ответа на вопрос.

Если пользователь введет числовое значение, input()вернет строку.

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

Итак, eval()оценит возвращаемое значение (или выражение), которое является строкой, и вернет целое число / число с плавающей запятой.

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

Конечно, это плохая практика. int()или float()следует использовать вместо eval()этого.

>>> float(input('Enter a number: '))
Enter a number: 3.14
3.14
Кэлвин Ким
источник
3

Другой вариант, если вы хотите ограничить строку оценки простыми литералами, это использовать ast.literal_eval(). Некоторые примеры:

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}

Из документов :

Безопасно оцените узел выражения или строку, содержащую литерал Python или отображение контейнера. Предоставленная строка или узел могут состоять только из следующих литеральных структур Python: строк, байтов, чисел, кортежей, списков, диктов, множеств, логических значений и None.

Это можно использовать для безопасной оценки строк, содержащих значения Python из ненадежных источников, без необходимости разбора значений самостоятельно. Он не способен вычислять произвольно сложные выражения, например, с использованием операторов или индексации.

Что касается того, почему это так ограничено, из списка рассылки :

Разрешение выражений операторов с литералами возможно, но гораздо сложнее, чем текущая реализация. Простая реализация небезопасна: вы можете без усилий вызывать практически неограниченное использование процессора и памяти (попробуйте «9 ** 9 ** 9» или «[None] * 9 ** 9»).

Что касается полезности, то эта функция полезна для «чтения» литеральных значений и контейнеров в виде строки repr (). Это может, например, использоваться для сериализации в формате, который похож на, но более мощный, чем JSON.

Брайан Бернс
источник
1
ast.literal_evalне поддерживает операторов, вопреки вашему '1+1'примеру. Тем не менее, он поддерживает списки, числа, строки и т. Д., И поэтому является хорошей альтернативой для обычных evalслучаев использования.
Бенджимин
@ Benjimin О, вы правы - это просто причуды, что он принимает 1 + 1! stackoverflow.com/questions/40584417/…
Брайан Бернс