Что делает Python eval ()? - PullRequest
       57

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

254 голосов
/ 21 февраля 2012

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

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

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

Ответы [ 10 ]

230 голосов
/ 21 февраля 2012

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

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

>>> x = 1
>>> eval('x + 1')
2
>>> eval('x')
1
142 голосов
/ 21 февраля 2012

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

42 голосов
/ 27 апреля 2017

Здесь много хороших ответов, но ни один не описывает использование eval() в контексте его globals и locals kwargs, то есть 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 может быть безопасно использован для большой стоимости.

N.B.

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

>>>from os import cpu_count
>>>eval('[1,cores]', {'__builtins__': None}, {'cores': cpu_count()})
[1, 8]
27 голосов
/ 21 февраля 2012

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

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

6 голосов
/ 21 февраля 2012

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

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

Подробнее о eval здесь .

5 голосов
/ 24 июня 2015

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

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

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

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

5 голосов
/ 27 марта 2015

Одним из полезных приложений 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'}
5 голосов
/ 21 февраля 2012

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

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

3 голосов
/ 13 июня 2018

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

Если пользователь вводит числовое значение, 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

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

>>> float(input('Enter a number: '))
Enter a number: 3.14
3.14
2 голосов
/ 28 ноября 2018

Другой вариант, если вы хотите ограничить строку оценки простыми литералами, это использовать 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» или «[Нет] * 9 ** 9»).

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

...