Що робить 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.