В книге, которую я читаю на Python, она продолжает использовать код eval(input('blah'))
Я прочитал документацию, и я это понимаю, но я до сих пор не вижу, как она меняет функцию input()
.
Что он делает? Может кто-нибудь объяснить?
В книге, которую я читаю на Python, она продолжает использовать код eval(input('blah'))
Я прочитал документацию, и я это понимаю, но я до сих пор не вижу, как она меняет функцию input()
.
Что он делает? Может кто-нибудь объяснить?
Функция eval позволяет программе Python запускать код Python внутри себя.
Пример eval (интерактивная оболочка):
>>> x = 1
>>> eval('x + 1')
2
>>> eval('x')
1
eval()
интерпретирует строку как код. Причина, по которой так много людей предупреждала вас об использовании этого, заключается в том, что пользователь может использовать это как вариант для запуска кода на компьютере. Если вы импортировали eval(input())
и os
, человек может ввести input()
os.system('rm -R *')
, который удалит все ваши файлы в вашем домашнем каталоге. (Предположим, что у вас есть система unix). Использование eval()
- это дыра в безопасности. Если вам нужно преобразовать строки в другие форматы, попробуйте использовать те вещи, которые делают это, например int()
.
Много хороших ответов здесь, но никто не описывает использование 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
можно безопасно использовать для получения высокой стоимости.
NB
Что еще хорошо в этих kwargs
это то, что вы можете начать использовать сокращение для своего кода. Допустим, вы используете eval как часть конвейера для выполнения некоторого импортированного текста. Текст не обязательно должен содержать точный код, он может соответствовать какому-либо формату файла шаблона и при этом выполнять все, что вы захотите. Например:
>>>from os import cpu_count
>>>eval('[1,cores]', {'__builtins__': None}, {'cores': cpu_count()})
[1, 8]
В Python 2.x input(...)
эквивалентно eval(raw_input(...))
, в Python 3.x raw_input
было переименовано input
, которое, как я подозреваю, привело к вашей путанице (вы, вероятно, смотрели документацию для input
в Python 2.x). Кроме того, eval(input(...))
отлично работает в Python 3.x, но поднимет TypeError
в Python 2.
В этом случае eval
используется для принуждения строки, возвращаемой из input
, в выражение и интерпретируется. Как правило, это считается плохой практикой.
eval()
оценивает переданную строку как выражение Python и возвращает результат. Например, eval("1 + 1")
интерпретирует и выполняет выражение "1 + 1"
и возвращает результат (2).
Одна из причин, по которой вы можете быть сбиты с толку, заключается в том, что приведенный вами код включает в себя определенный уровень косвенности. Внутренний вызов функции (input) выполняется первым, поэтому пользователь видит подсказку "blah". Давайте представим, что они отвечают "1 + 1" (кавычки добавлены для ясности, не вводите их при запуске вашей программы), функция ввода возвращает эту строку, которая затем передается внешней функции (eval), которая интерпретирует строку и возвращает результат (2).
Узнайте больше о Eval здесь.
Может быть, вводящий в заблуждение пример чтения строки и ее интерпретации.
Попробуйте eval(input())
и введите "1+1"
- это должно напечатать 2
. Eval оценивает выражения.
Одним из полезных приложений 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'}
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.
Я опоздал, чтобы ответить на этот вопрос, но, кажется, никто не дает четкого ответа на вопрос.
Если пользователь вводит числовое значение, 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()
этом случае вместо eval()
следует использовать int()
или float()
.
>>> float(input('Enter a number: '))
Enter a number: 3.14
3.14
Другой вариант, если вы хотите ограничить строку оценки простыми литералами, это использовать 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.
eval()
делает строку с целыми числами в ней математическим уравнением (все в строке воспринимается как текст, поэтому целые числа в этом случае означают числа как текст), поэтому, если какая-либо помощь нужна там, она есть.