Расчет значения выражения в Python для чисел, переменных и коллекций
Вычисление значения выражения в Python
Выражение в Python может быть частью кода или строкой. В первом случае интерпретатор вычисляет его при выполнении. Во втором нужен отдельный механизм: eval, compile, ast или специализированная библиотека. Выбор зависит от источника данных, требований безопасности и типа результата.
Основной способ для контролируемой строки: функция eval с пустым словарём встроенных имён и явным словарём переменных.
expression = '2 + 3 * 4'
variables = {'x': 5, 'y': 2}
result = eval(expression, {'__builtins__': {}}, variables)
print(result)вычислите значение выражения python (вычисление значения выражения в python)
14
команды языка python (команды python)
Пояснение шагов: строка expression содержит арифметическое выражение; второй аргумент {'__builtins__': {}} отключает доступ к стандартным функциям; третий аргумент передаёт значения переменных. Для выражения с переменной можно записать expr = 'x * y + 1', тогда результат будет равен 11.
expr = 'x * y + 1'
print(eval(expr, {'__builtins__': {}}, {'x': 5, 'y': 2}))оператор python примеры (примеры операторов в python)
11
Проблема безопасности. eval выполняет не только арифметику, но и произвольный Python-код. Для данных из недоверенного источника этот способ не подходит. Даже ограничение __builtins__ не даёт полной изоляции, поскольку остаются атрибуты объектов и другие возможности обхода.
# Опасный пример: ввод не должен попадать в eval без проверки
user_input = '__import__("os").system("echo hello")'
# eval(user_input) выполнит команду оболочки
Решение: применять ast.literal_eval для литералов или собственный обход AST с белым списком операторов. Для формул из доверенных настроек можно использовать eval с ограниченным словарём.
Как вычислить выражение без eval для безопасности?
Модуль ast позволяет разобрать выражение в дерево и вычислить только разрешённые узлы. Этот вариант подходит для пользовательских формул с числами, переменными и базовыми операциями.
import ast
import operator
allowed_bin = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Pow: operator.pow,
ast.Mod: operator.mod,
}
allowed_unary = {ast.UAdd: operator.pos, ast.USub: operator.neg}
def safe_eval(node, variables):
if isinstance(node, ast.Expression):
return safe_eval(node.body, variables)
if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
return node.value
if isinstance(node, ast.Name):
return variables[node.id]
if isinstance(node, ast.BinOp):
op = allowed_bin[type(node.op)]
return op(safe_eval(node.left, variables), safe_eval(node.right, variables))
if isinstance(node, ast.UnaryOp):
op = allowed_unary[type(node.op)]
return op(safe_eval(node.operand, variables))
raise ValueError('Недопустимый узел выражения')
expr = 'x * (y + 2) / 4'
tree = ast.parse(expr, mode='eval')
print(safe_eval(tree, {'x': 10, 'y': 6}))
20.0
Пояснение: ast.parse создаёт дерево без выполнения кода. Функция safe_eval спускается по узлам и вызывает только операторы из словаря. Неизвестные конструкции вызывают исключение. Такой подход удобен для калькуляторов, конфигурационных формул и правил расчета.
Как вычислить выражение, записанное прямо в коде?
Если выражение известно на этапе написания программы, отдельная функция не нужна. Интерпретатор сам выполняет операторы с учётом приоритетов.
a = 12
b = 5
result = a * b + a / b
print(result)
62.4
Приоритет операций: сначала умножение и деление, затем сложение и вычитание. Скобки меняют порядок. Этот вариант даёт максимальную скорость и проверку синтаксиса на этапе компиляции.
Как вычислить математическую формулу с функциями sin, cos, sqrt?
Для функций из math можно передать их в словарь имён для eval. Важно не открывать доступ к встроенным функциям Python.
import math
scope = {
'sin': math.sin,
'cos': math.cos,
'sqrt': math.sqrt,
'pi': math.pi,
'__builtins__': {},
}
expr = 'sqrt(25) + cos(pi) * 2'
print(eval(expr, scope))
3.0
Пояснение: sqrt(25) даёт 5.0, cos(pi) даёт -1.0, умножение на 2 даёт -2.0, итог равен 3.0. Если формула приходит от пользователя, лучше пропускать её через безопасный AST-обход и отдельно разрешать имена функций.
Как вычислить выражение для таблицы DataFrame?
В pandas есть метод DataFrame.eval, который вычисляет выражение над колонками без создания промежуточных объектов.
import pandas as pd
df = pd.DataFrame({'price': [100, 250, 80], 'count': [2, 1, 5]})
df['total'] = df.eval('price * count')
print(df)
price count total 0 100 2 200 1 250 1 250 2 80 5 400
Метод понимает имена колонок, арифметические операции и часть функций. Для сложных расчетов с внешними переменными используется параметр local_dict или @ перед именем переменной.
tax = 0.2
df['with_tax'] = df.eval('price * count * (1 + @tax)')
print(df[['total', 'with_tax']])
total with_tax 0 200 240.0 1 250 300.0 2 400 480.0
Как вычислить символьное выражение и подставить значения?
Библиотека SymPy работает с формулами как с символами. Это удобно для алгебраических преобразований, производных и точных дробей.
from sympy import symbols, sympify
x, y = symbols('x y')
expr = sympify('x**2 + 2*x + 1')
result = expr.subs({x: 3})
print(result)
16
Пояснение: sympify преобразует строку в символьное выражение. Метод subs заменяет символ на число. Для десятичных значений можно использовать Rational или N.
Как вычислить выражение с высокой точностью?
Для финансовых и научных расчетов с фиксированной точностью применяется decimal.Decimal. Обычные числа с плавающей точкой дают артефакты вроде 0.30000000000000004.
from decimal import Decimal, getcontext
getcontext().prec = 28
expr = 'Decimal("0.1") + Decimal("0.2")'
result = eval(expr, {'Decimal': Decimal, '__builtins__': {}})
print(result)
0.3
Пояснение: контекст задаёт 28 значащих цифр. Строки при создании Decimal сохраняют точное десятичное представление. Для формул с переменными можно подставлять Decimal-значения через словарь locals.
Типичные ошибки.
NameErrorвозникает, когда переменная не передана в словарь имён или отсутствует в области видимости.SyntaxErrorпоявляется при неверной строке: пропущенной скобке, лишнем операторе или тексте вместо числа.ZeroDivisionErrorвозникает при делении на ноль. Для пользовательских формул полезна проверка знаменателя.- Целочисленное деление
//и остаток%работают по правилам Python, что может отличаться от ожиданий в других языках. - Переполнение стека или большое число операций в
evalможет привести к высокой нагрузке. Для недоверенных данных нужны ограничения длины и времени.
try:
print(eval('10 / 0', {'__builtins__': {}}, {}))
except ZeroDivisionError as err:
print('Ошибка:', err)
Ошибка: division by zero
Расширенные примеры вычисления выражений в Python
Ниже собраны примеры для разных типов выражений: от безопасного AST-калькулятора до векторных расчетов NumPy и точной арифметики Decimal.
Безопасный калькулятор с функциями math через ast
import ast
import math
import operator
allowed_bin = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Pow: operator.pow,
ast.Mod: operator.mod,
}
allowed_unary = {ast.UAdd: operator.pos, ast.USub: operator.neg}
allowed_funcs = {
'sqrt': math.sqrt,
'sin': math.sin,
'cos': math.cos,
'abs': abs,
}
def safe_eval(node, variables):
if isinstance(node, ast.Expression):
return safe_eval(node.body, variables)
if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
return node.value
if isinstance(node, ast.Name):
return variables[node.id]
if isinstance(node, ast.BinOp):
op = allowed_bin[type(node.op)]
return op(safe_eval(node.left, variables), safe_eval(node.right, variables))
if isinstance(node, ast.UnaryOp):
op = allowed_unary[type(node.op)]
return op(safe_eval(node.operand, variables))
if isinstance(node, ast.Call):
if isinstance(node.func, ast.Name) and node.func.id in allowed_funcs:
args = [safe_eval(arg, variables) for arg in node.args]
return allowed_funcs[node.func.id](*args)
raise ValueError('Недопустимый узел выражения')
expr = 'sqrt(x**2 + y**2) + abs(-3)'
tree = ast.parse(expr, mode='eval')
print(safe_eval(tree, {'x': 3, 'y': 4}))
8.0
Пояснение: выражение sqrt(3**2 + 4**2) даёт 5.0, функция abs(-3) даёт 3, итог равен 8.0. Такой калькулятор не выполняет произвольные вызовы и подходит для формул из внешнего интерфейса.
Компиляция выражения для многократного расчета
code = compile('base * rate + bonus', '', 'eval')
records = [
{'base': 100, 'rate': 0.2, 'bonus': 10},
{'base': 200, 'rate': 0.1, 'bonus': 30},
]
for record in records:
print(eval(code, {'__builtins__': {}}, record))
30.0 50.0
Пояснение: compile создаёт объект кода один раз. Далее eval использует его с разными словарями переменных. Это удобно для пакетной обработки строк с одинаковой структурой формулы.
Вычисление выражения в pandas с внешней переменной
import pandas as pd
df = pd.DataFrame({
'qty': [2, 3, 5],
'price': [10, 20, 8],
})
discount = 0.1
df['final'] = df.eval('qty * price * (1 - @discount)')
print(df)
qty price final 0 2 10 18.0 1 3 20 54.0 2 5 8 36.0
Пояснение: символ @ показывает pandas, что discount является внешней переменной Python, а не колонкой DataFrame.
Векторное выражение с NumPy
import numpy as np
x = np.array([1, 2, 3])
y = np.array([10, 20, 30])
expr = 'x ** 2 + y / 2'
result = eval(expr, {'__builtins__': {}}, {'x': x, 'y': y})
print(result)
[ 6. 14. 24.]
Пояснение: x ** 2 даёт массив [1, 4, 9], y / 2 даёт [5, 10, 15]. Сложение выполняется поэлементно, результат имеет тип float.
Символьное выражение, производная и численный расчет
from sympy import symbols, diff, lambdify
x = symbols('x')
expr = x**3 - 3*x + 2
derivative = diff(expr, x)
f = lambdify(x, expr, 'math')
g = lambdify(x, derivative, 'math')
print(f(2), g(2))
4 9
Пояснение: исходная функция при x = 2 равна 4, производная 3*x**2 - 3 при x = 2 равна 9. SymPy сохраняет точную формулу, а lambdify создаёт быструю функцию для чисел.
Точная арифметика Decimal с переменными
from decimal import Decimal, getcontext, ROUND_HALF_UP
getcontext().prec = 10
price = Decimal('19.99')
qty = Decimal('3')
tax = Decimal('0.075')
expr = 'price * qty * (1 + tax)'
result = eval(expr, {'__builtins__': {}}, {'price': price, 'qty': qty, 'tax': tax})
print(result.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))
64.47
Пояснение: 19.99 * 3 * 1.075 даёт 64.46775. Метод quantize округляет до копейки с правилом ROUND_HALF_UP.
literal_eval для конфигурационных значений
from ast import literal_eval
text = "{'width': 10, 'height': 20, 'ratio': 0.5}"
config = literal_eval(text)
print(config['width'] * config['height'] * (1 + config['ratio']))
300.0
Пояснение: literal_eval безопасно разбирает строки, числа, кортежи, списки и словари. Он не вычисляет арифметические операции вроде 2 + 3, поэтому для формул нужен другой механизм.
eval с перегрузкой операторов пользовательского класса
class Money:
def __init__(self, amount):
self.amount = amount
def __add__(self, other):
return Money(self.amount + other.amount)
def __mul__(self, k):
return Money(self.amount * k)
def __repr__(self):
return f'Money({self.amount})'
a = Money(100)
b = Money(50)
result = eval('a + b * 2', {'__builtins__': {}}, {'a': a, 'b': b})
print(result)
Money(200)
Пояснение: eval использует те же правила операторов Python. Если объекты поддерживают __add__ и __mul__, выражение работает с ними как с числами.
Ограничение literal_eval на арифметике
from ast import literal_eval
try:
print(literal_eval('2 + 3'))
except ValueError as err:
print('ValueError:', err)
ValueError: malformed node or string
Пояснение: literal_eval принимает только литералы. Для арифметики с числами и переменными подходит безопасный AST-обход или eval с ограничениями.