Вычисления значений выражений с помощью Python

Раздел: Математика -> Математические вычисления

Вычисление выражений: обзор методов

Как вычислить значение математического выражения, введённого пользователем, с минимальным риском для безопасности?

Наиболее эффективным решением для типовых задач является использование встроенной функции eval с ограниченным пространством имён. Это позволяет выполнить арифметические операции, а также вызывать функции из модуля math, одновременно блокируя доступ к опасным объектам Python.

import math

def safe_eval(expr):
    allowed = {k: v for k, v in math.__dict__.items() if not k.startswith('_')}
    allowed['__builtins__'] = None
    return eval(expr, allowed, {})

print(safe_eval('sin(pi/4)**2 + cos(pi/4)**2'))   # 1.0
print(safe_eval('sqrt(144)'))                    # 12.0

Python решение примера (решение примера на python)

Важно

При таком подходе пользователь не сможет обратиться к функциям ввода-вывода, импорту или системным командам. Для дополнительной защиты можно парсить выражение через ast.parse и проверять допустимые узлы.

Типичные ошибки

  • SyntaxError – неверный синтаксис выражения. Решение: обернуть вызов в try-except.
  • NameError – использование неизвестных имён (например, переменных, не переданных в словарь). Решение: определить все допустимые имена в allowed или передавать переменные через второй аргумент.
  • ZeroDivisionError – деление на ноль. Решение: обработать исключение или использовать модуль decimal для контролируемой точности.

Цель – выполнение произвольных формул, вводимых извне, на сервере или в десктопном приложении, где важна скорость и относительная безопасность.

Когда безопасность критична и выражение состоит только из литералов?

Используйте ast.literal_eval. Он умеет вычислять строки, числа, кортежи, списки, словари, множества и булевы значения, но не операторы. Подходит для разбора JSON-подобных данных, конфигурационных файлов.

import ast
expr = "[1, 2, 3]"
result = ast.literal_eval(expr)
print(result, type(result))  # [1, 2, 3] <class 'list'>

Python вычисление значения выражений (вычисление значения выражений в python)

Ошибка: ValueError при попытке вычислить выражение с операторами. Решение: не использовать literal_eval для формул с арифметикой.

Как выполнить символьное вычисление, например, найти производную или интеграл?

Применяйте библиотеку sympy. Она позволяет работать с математическими символами и выполнять аналитические преобразования.

from sympy import symbols, diff, sin, cos, lambdify

x = symbols('x')
func = sin(x)**2 + cos(x)**2
diff_func = diff(func, x)
print(diff_func)  # 0

# численное вычисление
f_num = lambdify(x, func, 'numpy')
print(f_num(3.14))  # 1.0

вычисление функции в python (вычисление значения функции в python)

Проблема: sympy медленнее eval при большом количестве подстановок. Используйте lambdify для преобразования в быструю численную функцию.

Цель – математическое моделирование, проверка тождеств, обучение.

Как вычислить выражение с векторизацией для больших массивов данных?

Библиотека numexpr компилирует выражение в эффективный машинный код и работает с массивами NumPy.

import numexpr as ne
import numpy as np

a = np.random.rand(1000000)
b = np.random.rand(1000000)
result = ne.evaluate('a + b * sin(a)')

Python вычисление корня (вычисление квадратного корня в python)

Ошибка: если переменные не являются массивами одинаковой длины. Решение: привести к одному типу или использовать вещание.

Цель – ускорение вычислений над крупными наборами данных.

Как реализовать собственный парсер выражений для учебных целей или специфических ограничений?

Можно написать алгоритм сортировочной станции (Дейкстра) для преобразования инфиксной записи в обратную польскую (RPN) и затем вычислить RPN с помощью стека. Это даёт полный контроль над операторами и функциями.

def rpn(expr):
    # упрощённая реализация для +, -, *, / 
    ... 
    # (полный код опущен для краткости)

Сложность: поддержка унарных минусов, функций с переменным числом аргументов. Решение: тщательная обработка токенов.

Цель – обучение, встраивание в продукты с очень жёсткими требованиями безопасности.

- Python вычислить строку (вычисление выражения из строки в python)
- возвести в квадрат python (возведение числа в квадрат в python)
- площадь python (вычисление площади в python)

Расширенные примеры вычислений

Пример 1: безопасный eval с передачей переменных

Пример
import math

def var_eval(expr, **variables):
    allowed = {k: v for k, v in math.__dict__.items() if not k.startswith('_')}
    allowed['__builtins__'] = None
    allowed.update(variables)
    return eval(expr, allowed, {})

result = var_eval('a * b + c', a=2, b=3, c=4)
print(result)  # 10
10

Таким образом можно вычислять формулы с любыми переменными, не опасаясь доступа к системным функциям.

Пример 2: использование ast для парсинга и разрешения только определённых операций

Пример
import ast

def safe_eval_ast(expr, allowed_nodes=None):
    if allowed_nodes is None:
        allowed_nodes = {ast.Expression, ast.BinOp, ast.UnaryOp, ast.Constant,
                        ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Pow, ast.USub}
    tree = ast.parse(expr, mode='eval')
    for node in ast.walk(tree):
        if type(node) not in allowed_nodes:
            raise ValueError(f"Запрещённый узел: {type(node)}")
    return eval(compile(tree, '', 'eval'), {'__builtins__': None}, {})

print(safe_eval_ast('3 + 4 * 2'))      # 11
print(safe_eval_ast('[1,2,3]'))        # ValueError (запрещён List)
11
Traceback ... ValueError: Запрещённый узел: <class 'ast.List'>

Данный подход позволяет гибко ограничивать использование Python-синтаксиса.

Пример 3: символьная производная и подстановка значений через sympy

Пример
from sympy import symbols, diff, sin, exp, lambdify

x, y = symbols('x y')
func = sin(x) * exp(-y**2)
derivative = diff(func, x)
print(derivative)  # exp(-y**2)*cos(x)

f_num = lambdify((x, y), func, 'numpy')
val = f_num(0.5, 1.0)
print(val)
exp(-y**2)*cos(x)
0.306054...  # численное значение

Символьные вычисления полезны для аналитического упрощения выражений.

Пример 4: вычисление выражений с условным оператором (тернарник) через eval

Пример
expr = '10 if a > 5 else 20'
result = eval(expr, {'__builtins__': None}, {'a': 7})
print(result)  # 10
10

С помощью eval можно выполнять условные конструкции, но подобные выражения следует тщательно проверять на безопасность.

Пример 5: парсер обратной польской записи (RPN) для четырёх арифметических операций

Пример
def tokenize(expr):
    import re
    return re.findall(r'\d+\.?\d*|[+\-*/()]', expr)

def to_rpn(tokens):
    # реализация сортировочной станции (упрощённо)
    pass  # полный код не приводится

def eval_rpn(rpn):
    stack = []
    for tok in rpn:
        if tok in '+-*/':
            b = stack.pop()
            a = stack.pop()
            if tok == '+': stack.append(a+b)
            elif tok == '-': stack.append(a-b)
            elif tok == '*': stack.append(a*b)
            elif tok == '/': stack.append(a/b)
        else:
            stack.append(float(tok))
    return stack[0]

# Пример использования
expr = '3 + 4 * 2'
tokens = tokenize(expr)
rpn = to_rpn(tokens)
print(eval_rpn(rpn))  # 11
11

Такой парсер даёт полный контроль над операциями и не зависит от встроенного eval, что полезно в окружениях с жёсткими ограничениями.

Вычисление значения выражений в Python - comments

En
Python вычисление значения выражений (python)