У книзі, яку я читаю на 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
і не може робити те, що робить eval
.
eval
, окрім того, що є незахищеним, не може запускати цілі програми, як це робить кодовий блок, оскільки він може оцінювати лише один вираз.
eval()
інтерпретує рядок як код. Причина, по якій так багато людей попереджало вас про використання цього, полягає в тому, що користувач може використовувати це як опцію для запуску коду на комп'ютері. Якщо у вас є eval(input())
та os
імпортовано, людина може набрати текст, input()
os.system('rm -R *')
який видалить усі ваші файли у вашому домашньому каталозі. (Припустимо, що у вас є система Unix). Використання eval()
- це отвір для захисту. Якщо вам потрібно конвертувати рядки в інші формати, спробуйте використовувати такі речі, як int()
.
eval
з input()
- це отвір для безпеки. Не вкладайте input()
всередину заяву eval, і ви будете добре.
eval
, питання безпеки у багатьох випадках.
input
зазвичай бере свої дані з консолі, користувач може просто вийти з програми і ввести rm -R *
все одно ...
Тут багато хороших відповідей, але жодна не описує використання 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
у вираз та інтерпретованого. Як правило, це вважається поганою практикою.
input
означає, що було raw_input
зроблено в 2.x.
Може бути оманливим прикладом читання рядка та його інтерпретації.
Спробуйте eval(input())
ввести "1+1"
- це слід надрукувати 2
. Еваль оцінює вирази.
eval()
оцінює пройдений рядок як вираз Python і повертає результат. Наприклад, eval("1 + 1")
інтерпретує і виконує вираз"1 + 1"
та повертає результат (2).
Однією з причин, що вас можуть збентежити, є те, що вказаний вами код включає рівень непрямості. Внутрішній виклик функції (вхід) виконується спочатку, щоб користувач бачив підказку "бла". Давайте уявимо, що вони відповідають "1 + 1" (цитати додані для наочності, не вводьте їх під час запуску програми), функція введення повертає ту рядок, який потім передається зовнішній функції (eval), яка інтерпретує рядок і повертає результат (2).
Детальніше про eval читайте тут .
eval()
, як підказує назва, оцінює переданий аргумент.
raw_input()
зараз input()
у версії python 3.x. Тож найчастіше зустрічається приклад використання - eval()
це використання функціональних можливостей, що input()
надаються у 2.x версії python. raw_input повернув введені користувачем дані як рядок, а вхід оцінив значення введених даних і повернув їх.
eval(input("bla bla"))
Таким чином, копіюється функціональність input()
в 2.x, тобто, оцінюванні даних, що вводяться користувачем.
Коротше кажучи: eval()
оцінює передані до нього аргументи і, отже, eval('1 + 1')
повертає 2.
Одним із корисних застосувань програми 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
робить?
Я спізнююсь відповісти на це запитання, але, схоже, ніхто не дає чіткої відповіді на питання.
Якщо користувач введе числове значення, input()
поверне рядок.
>>> input('Enter a number: ')
Enter a number: 3
>>> '3'
>>> input('Enter a number: ')
Enter a number: 1+1
'1+1'
Отже, eval()
буде оцінено повернене значення (або вираз), яке є рядком, і повернути ціле число / float.
>>> 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
Інший варіант, якщо ви хочете обмежити рядок оцінки простими літералами, - це використовувати 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.
ast.literal_eval
не підтримує операторів, всупереч вашому '1+1'
прикладу. Тим не менш, він містить списки підтримки, цифри, рядки тощо, і тому є хорошою альтернативою для загальних eval
випадків використання.