Включите исполнение JavaScript в браузере, чтобы запустить приложение.
25 фев 2025

Как использовать метод eval() в Python

Функция eval() в Python — это полезный инструмент для некоторых задач. Однако у него есть недостаток в виде снижения безопасности, поэтому важно применять его правильно. В этой статье рассмотрим, как он работает, примеры, а также детали, которые стоит учитывать при использовании.

Как работает eval() в Python

Функция принимает в качестве аргумента строку, выполняет ее как выражение на Python и возвращает результат. В нее можно передавать выражения, которые возвращают результат, например выполнять математические операции. При этом нельзя передавать целые блоки кода (составные конструкции). Другими словами, запрещено передавать в качестве аргументов условные конструкции с if, циклы с for (для генерации списков, словарей и других объектов использовать for можно) и while, строки с ключевыми словами import, def, class. Если попробовать передать такие аргументы, то вернется SyntaxError.

Рассмотрим синтаксис:

eval(expression, globals=None, locals=None)
py

Описание параметров:

  • expression (обязательный) — строка, содержащая выражение, которое нужно выполнить;
  • globals (необязательный) — словарь с доступными глобальными переменными и методами (если None, то могут использоваться все текущие глобальные переменные и методы);
  • locals (необязательный) — словарь с доступными локальными методами и переменными (если None, то совпадает с глобальной областью видимости).

Безопасность

Использование eval() может привести к снижению безопасности программы, потому что:

  • она выполняет произвольный код, который может передать, в том числе злоумышленник, например, такая строка удаляет все файлы в текущей директории без подтверждения:
"__import__('subprocess').getoutput('rm –rf *')"
py
  • по умолчанию у нее есть доступ ко всем встроенным в Python функциям и объектам, а также к переменным текущей области;
  • злоумышленники могут находить способы получения доступа даже в случае, если две проблемы выше решены.

Поэтому eval() не рекомендуется применять без крайней необходимости, но если ее все же включили в код, то есть несколько способов обезопасить программу:

  • ограничить глобальную и локальную области видимости
# Все переменные, кроме a, b и c будут недоступны

expression = "a * b + c"

result = eval(expression, {"a": 2, "b": 3}, {"c": 5})

print(result)  

# Вывод: 11
py
  • ограничить доступ к __builtins__ (встроенному в Python объекту, содержащему стандартные функции, объекты и исключения)
# Так будет недоступен весь словарь

expression = "sum([1, 2, 3])"

eval(expression, {"__builtins__": {}}, {})

# Вывод: NameError: name 'sum' is not defined

# Если доступ к определенным элементам нужен, то можно предоставить его так

expression = "sum([1, 2, 3])"

eval(expression, {"__builtins__": {}, "sum": sum}, {})

# Вывод: 6
py
  • ограничить имена переменных, функций и других объектов, которые могут использоваться в выражении, переданном на вход
# Можно использовать литералы, операторы и две функции: min() и len()

def safe_eval(expression):

    allowed_functions = {"min": min, "len": len}  

    compiled_code = compile(expression, "<string>", "eval")  

    for name in compiled_code.co_names:  

        if name not in allowed_functions:  

            raise NameError(f"Использовать '{name}' нельзя.")

    return eval(compiled_code, {"__builtins__": {}}, allowed_functions)

print(safe_eval("min([1, 2, 3])"))  

# Вывод: 1

print(safe_eval("len([1, 2, 3])"))  

# Вывод: 3

print(safe_eval("sum([1, 2, 3])"))  

# Вывод: NameError…
py
  • исключить использование имен полностью (если это возможно)
# Можно использовать только литералы и операторы

def eval_literals_only(expression):

    compiled_code = compile(expression, "<string>", "eval")

    if compiled_code.co_names:

        raise NameError("Выполнить операцию нельзя.")

    return eval(compiled_code , {"__builtins__": {}}, {})

print(eval_literals_only("3.5 + 2.5"))  

# Вывод: 6.0

print(eval_literals_only("abs(-5)"))  

# Вывод: NameError…
py
  • использовать literal_eval() — функцию, которая способна обработать строки, содержащие только литералы (операторы также не поддерживаются)
from ast import literal_eval

expression = "{'key1': 1, 'key2': 2}"

print(literal_eval(expression))  

# Вывод: {'key1': 1, 'key2': 2}

# Такое выражение обработано не будет

expression = "3 + 2"

print(literal_eval(expression))  

# ValueError...
py

Примеры использования

Рассмотрим несколько простых примеров использования eval():

# Математическая операция

expression = "5 + 3 * (3 - 1)"

result = eval(expression)

print(result)  

# Вывод: 11

# Обращение к переменным

x = 15

y = 115

expression = "x + y"

result = eval(expression)

print(result)  

# Вывод: 130

# Вызов функции

def multiply(a, b):

    return a * b

expression = "multiply(3, 5)"

result = eval(expression)

print(result)  

# Вывод: 15

# Оценка булева выражения

x = 5

print(eval('x == 4'))

# Вывод: False
py

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

  • в программах, где пользователи могут задавать собственные фрагменты кода для того, чтобы изменить их поведение под свои потребности. Допустим, есть программа для работы с текстами, в которой пользователи могут преобразовывать свой текст — переводить все символы в верхний регистр и менять их местами:
def process_text(user_script, input_text):

    allowed_functions = {"upper": str.upper, "replace": str.replace}

    compiled_code = compile(user_script, "<string>", "eval")

    for name in compiled_code.co_names:

        if name not in allowed_functions and name != "text":

            raise NameError(f"Использовать '{name}' нельзя.")

    return eval(compiled_code, {"__builtins__": None, "text": input_text}, allowed_functions)

user_input = input("Введите выражение для обработки текста: ")

input_text = input("Введите текст: ")

try:

    result = process_text(user_input, input_text)

    print(f"Результат: {result}")

except Exception as e:

    print(f"Ошибка: {e}")

# Допустим, пользователь ввел команду text.upper().replace('N', 'M') и текст «Line»

# Вывод: LIME

# Теперь пользователь ввел text.lower().replace('N', 'M') и текст «Line»

# Вывод: Ошибка: Использовать 'lower' нельзя.

в приложениях, где пользователи могут ввести математическое выражение, которое нужно вычислить:

def calculate_expression(user_input):

    compiled_code = compile(user_input, "<string>", "eval")

    if compiled_code.co_names:

        raise NameError("Использовать функции нельзя.")

    return eval(compiled_code, {"__builtins__": None}, {})

user_input = input("Введите математическое выражение: ")

try:

    result = calculate_expression(user_input)

    print(f"Результат: {result}")

except Exception as e:

    print(f"Ошибка: {e}")

# Допустим, пользователь ввел 15 - 3 

# Вывод: 12

# Теперь пользователь ввел abs(-5)

# Вывод: Ошибка: Использовать функции нельзя.
py

Главное

  • eval() — это встроенная в Python функция, которая принимает строку, анализирует и выполняет ее как код, возвращая результат.
  • Передавать в нее можно исключительно выражения: если передать составную конструкцию, то вернется SyntaxError.
  • В связи с риском возникновения уязвимостей, eval() лучше не использовать, когда можно обойтись без нее.

Если ее необходимо ввести в код, то стоит позаботиться о безопасности: ограничить области видимости и доступ к встроенным функциям и объектам Python. Обозначить те имена переменных, функций, методов и объектов, которые могут использоваться в строке, передаваемой на вход, или же исключить их использование вовсе, применять literal_eval().