Що робить eval () Python?


306

У книзі, яку я читаю на Python, він продовжує використовувати код eval(input('blah'))

Я читаю документацію, і я її розумію, але все ще не бачу, як вона змінюється input() функцію.

Що це робить? Може хтось пояснить?


4
Функція Eval намагається виконати та інтерпретувати переданий їй рядок (аргумент) як код python. x = 1 print (eval ('x + 1')) Вихід вищевказаного коду буде 2. Недоліком такого підходу є те, що користувач отримує незалежність від написання коду, що може спричинити загрозу умовам. Хоча ви можете обмежити користувачів від доступ до багатьох змінних та методів, передаючи глобальний та локальний параметр у функції eval.
ATIF IBAD KHAN

Відповіді:


276

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

Приклад eval (інтерактивна оболонка):

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

25
ха-ха, це був тривіальний приклад, але ви можете дозволити користувачеві ввести довільну команду і змусити python виконати її. Таким чином, ви можете мати тип користувача в командному рядку, а потім змусити python запустити його як код. Так, наприклад: eval ("__ import __ ('os"). Delete (' файл ') ").
BYS2

60
Це буде здаватися марним, поки ви не знайдете в цьому потреби. Він використовується на сайтах, таких як codepad.org, щоб дозволяти виконувати сценарії в тестовому середовищі. eval()може також використовуватися для виконання високодинамічного коду, але перед тим, як використовувати його, слід повністю зрозуміти ризики безпеки та продуктивності.
Джордж Куммінз

6
@GeorgeCummins, codepad.org не використовує evalі не може робити те, що робить eval.
Майк Грехем,

16
@GeorgeCummins: codepag.org запускає все у пісочній скриньці: тюрем chroot з ptrace перевіряє у віртуальній машині, щоб запобігти шкідливому коду нічого поганого. Набагато складніше, ніж простий евал. Також eval специфічний для Python. кодова панель підтримує купу мов.
FogleBird

4
@GeorgeCummins, кодова панель працює дуже складною системою для безпечного запуску довільних програм. eval, окрім того, що є незахищеним, не може запускати цілі програми, як це робить кодовий блок, оскільки він може оцінювати лише один вираз.
Майк Грехем,

165

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


14
Ви маєте на увазі використання evalз input()- це отвір для безпеки. Не вкладайте input()всередину заяву eval, і ви будете добре.
Ромер

19
@Rohmer, небезпечні дані можуть надходити звідусіль: веб-запити, поля введення форми, зчитування файлів, ... не лише з введення консолі. Навіть якщо ви пишете файли самостійно, вони все одно можуть містити вхід, який спочатку надходив з ненадійного джерела. Таким чином 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]

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у вираз та інтерпретованого. Як правило, це вважається поганою практикою.


Або це книга Python 3.x, де inputозначає, що було raw_inputзроблено в 2.x.
dan04

1
Так, це сталося зі мною після того, як я написав свою первинну відповідь, і це явно так.
zeekay

6

Може бути оманливим прикладом читання рядка та його інтерпретації.

Спробуйте eval(input())ввести "1+1"- це слід надрукувати 2. Еваль оцінює вирази.


Чому я повинен вводити його між цитатами? Введення - це отримання рядка, і передавання його в eval, а не виконання коду, тож я маю б добре, якщо я просто набрав 1 + 1 ... ¿?
JC Rocamonde

Вся справа в тому, що ви змішуєте P2.x і 3.x. У Python 2 ваш код працює, але не має сенсу двічі оцінюватися. У python 3 він не робить, і повертає рядок.
JC Rocamonde

6

eval()оцінює пройдений рядок як вираз Python і повертає результат. Наприклад, eval("1 + 1")інтерпретує і виконує вираз"1 + 1" та повертає результат (2).

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

Детальніше про eval читайте тут .


6

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.


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()буде оцінено повернене значення (або вираз), яке є рядком, і повернути ціле число / 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

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

@benjimin о, ти маєш рацію - це лише химерність, що вона приймає 1 + 1! stackoverflow.com/questions/40584417/…
Брайан Бернс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.