本文目录导读:

我来详细介绍如何实现一个公式运算脚本系统,我将从简单到复杂提供几种实现方案。
基础方案:使用内置函数
Python实现(最简单)
# 使用内置的eval函数(注意安全风险)
def calculate_safe(expression, variables=None):
"""安全计算表达式"""
if variables is None:
variables = {}
# 允许的数学函数和操作
allowed_names = {
'abs': abs, 'max': max, 'min': min,
'round': round, 'sum': sum,
'pow': pow, 'sqrt': lambda x: x**0.5
}
# 合并用户变量
allowed_names.update(variables)
try:
# 编译表达式
code = compile(expression, '<string>', 'eval')
# 验证代码是否为安全操作
for name in code.co_names:
if name not in allowed_names:
raise NameError(f"未允许的操作: {name}")
return eval(code, {"__builtins__": {}}, allowed_names)
except Exception as e:
return f"计算错误: {str(e)}"
# 使用示例
variables = {'x': 10, 'y': 5}
result = calculate_safe("x + y * 2", variables)
print(f"计算结果: {result}") # 输出: 20
自定义表达式解析器
使用词法分析和语法分析
import re
from enum import Enum
class TokenType(Enum):
NUMBER = 'NUMBER'
PLUS = 'PLUS'
MINUS = 'MINUS'
MULTIPLY = 'MULTIPLY'
DIVIDE = 'DIVIDE'
LPAREN = 'LPAREN'
RPAREN = 'RPAREN'
VARIABLE = 'VARIABLE'
POWER = 'POWER'
class Token:
def __init__(self, type, value):
self.type = type
self.value = value
class Lexer:
def __init__(self, text):
self.text = text
self.pos = 0
self.tokens = []
self.current_char = text[0] if text else None
def error(self):
raise Exception('非法字符')
def advance(self):
self.pos += 1
if self.pos < len(self.text):
self.current_char = self.text[self.pos]
else:
self.current_char = None
def skip_whitespace(self):
while self.current_char and self.current_char.isspace():
self.advance()
def number(self):
result = ''
while self.current_char and (self.current_char.isdigit() or self.current_char == '.'):
result += self.current_char
self.advance()
return float(result)
def variable(self):
result = ''
while self.current_char and (self.current_char.isalnum() or self.current_char == '_'):
result += self.current_char
self.advance()
return result
def tokenize(self):
while self.current_char:
if self.current_char.isspace():
self.skip_whitespace()
continue
if self.current_char.isdigit() or self.current_char == '.':
self.tokens.append(Token(TokenType.NUMBER, self.number()))
elif self.current_char.isalpha() or self.current_char == '_':
self.tokens.append(Token(TokenType.VARIABLE, self.variable()))
elif self.current_char == '+':
self.tokens.append(Token(TokenType.PLUS, '+'))
self.advance()
elif self.current_char == '-':
self.tokens.append(Token(TokenType.MINUS, '-'))
self.advance()
elif self.current_char == '*':
self.advance()
if self.current_char == '*':
self.tokens.append(Token(TokenType.POWER, '**'))
self.advance()
else:
self.tokens.append(Token(TokenType.MULTIPLY, '*'))
elif self.current_char == '/':
self.tokens.append(Token(TokenType.DIVIDE, '/'))
self.advance()
elif self.current_char == '(':
self.tokens.append(Token(TokenType.LPAREN, '('))
self.advance()
elif self.current_char == ')':
self.tokens.append(Token(TokenType.RPAREN, ')'))
self.advance()
else:
self.error()
return self.tokens
class Parser:
def __init__(self, tokens, variables=None):
self.tokens = tokens
self.pos = 0
self.current_token = tokens[0] if tokens else None
self.variables = variables or {}
def error(self):
raise Exception('语法错误')
def eat(self, token_type):
if self.current_token and self.current_token.type == token_type:
self.pos += 1
if self.pos < len(self.tokens):
self.current_token = self.tokens[self.pos]
else:
self.current_token = None
else:
self.error()
def factor(self):
token = self.current_token
if token.type == TokenType.NUMBER:
self.eat(TokenType.NUMBER)
return token.value
elif token.type == TokenType.VARIABLE:
self.eat(TokenType.VARIABLE)
if token.value in self.variables:
return self.variables[token.value]
else:
raise Exception(f"未定义的变量: {token.value}")
elif token.type == TokenType.LPAREN:
self.eat(TokenType.LPAREN)
result = self.expr()
self.eat(TokenType.RPAREN)
return result
elif token.type == TokenType.MINUS:
self.eat(TokenType.MINUS)
return -self.factor()
def term(self):
result = self.factor()
while self.current_token and self.current_token.type in (TokenType.MULTIPLY, TokenType.DIVIDE, TokenType.POWER):
token = self.current_token
if token.type == TokenType.MULTIPLY:
self.eat(TokenType.MULTIPLY)
result *= self.factor()
elif token.type == TokenType.DIVIDE:
self.eat(TokenType.DIVIDE)
divisor = self.factor()
if divisor == 0:
raise Exception("除零错误")
result /= divisor
elif token.type == TokenType.POWER:
self.eat(TokenType.POWER)
result **= self.factor()
return result
def expr(self):
result = self.term()
while self.current_token and self.current_token.type in (TokenType.PLUS, TokenType.MINUS):
token = self.current_token
if token.type == TokenType.PLUS:
self.eat(TokenType.PLUS)
result += self.term()
elif token.type == TokenType.MINUS:
self.eat(TokenType.MINUS)
result -= self.term()
return result
def parse(self):
return self.expr()
# 使用示例
def evaluate_expression(expression, variables=None):
lexer = Lexer(expression)
tokens = lexer.tokenize()
parser = Parser(tokens, variables)
return parser.parse()
# 测试
formula = "x * (y + 2) - 5"
vars = {'x': 10, 'y': 3}
result = evaluate_expression(formula, vars)
print(f"{formula} = {result}") # 输出: 45.0
使用专业库(推荐)
sympy库
import sympy as sp
# 定义符号变量
x, y = sp.symbols('x y')
# 公式定义
formula = x**2 + 2*x*y + y**2
# 代入数值
result = formula.subs({x: 3, y: 4})
print(f"计算结果: {result}") # 输出: 49
# 公式简化
simplified = sp.simplify((x+y)**2 - (x**2 + 2*x*y + y**2))
print(f"简化结果: {simplified}") # 输出: 0
# 求导
derivative = sp.diff(formula, x)
print(f"导数: {derivative}") # 输出: 2*x + 2*y
numexpr库(高性能)
import numexpr as ne
import numpy as np
# 简单计算
result = ne.evaluate('2*3 + 4')
print(f"简单计算: {result}")
# 向量计算
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
result = ne.evaluate('a * b + 10')
print(f"向量计算: {result}")
# 复杂公式
x = np.arange(10)
result = ne.evaluate('x**2 + 3*x + 1')
print(f"数组计算: {result}")
完整的公式引擎实现
import re
import math
from typing import Dict, Any, Optional
class FormulaEngine:
"""完整的公式计算引擎"""
# 内置函数映射
BUILTIN_FUNCTIONS = {
'abs': abs,
'max': max,
'min': min,
'pow': pow,
'sqrt': math.sqrt,
'sin': math.sin,
'cos': math.cos,
'tan': math.tan,
'log': math.log,
'log10': math.log10,
'exp': math.exp,
'ceil': math.ceil,
'floor': math.floor,
'round': round,
'pi': math.pi,
'e': math.e
}
def __init__(self):
self.variables = {}
self.custom_functions = {}
def set_variable(self, name: str, value: Any):
"""设置变量"""
self.variables[name] = value
def set_variables(self, variables: Dict[str, Any]):
"""批量设置变量"""
self.variables.update(variables)
def add_function(self, name: str, func):
"""添加自定义函数"""
self.custom_functions[name] = func
def validate_formula(self, formula: str) -> bool:
"""验证公式合法性"""
try:
# 检查括号匹配
stack = []
for char in formula:
if char == '(':
stack.append(char)
elif char == ')':
if not stack:
return False
stack.pop()
if stack:
return False
# 检查非法字符
valid_chars = r'[\w\s\+\-\*\/\(\)\.\,\^\%]'
for char in formula:
if char not in valid_chars and not char.isalpha() and not char.isdigit():
if char not in '_.':
return False
return True
except:
return False
def calculate(self, formula: str, **kwargs) -> Any:
"""计算公式"""
# 合并变量
all_vars = {**self.variables, **kwargs}
all_funcs = {**self.BUILTIN_FUNCTIONS, **self.custom_functions}
# 创建计算上下文
context = {**all_vars, **all_funcs}
try:
# 编译公式
code = compile(formula, '<string>', 'eval')
# 验证所有名称都在允许的范围内
for name in code.co_names:
if name not in context:
raise NameError(f"未定义的变量或函数: {name}")
# 安全执行
result = eval(code, {"__builtins__": {}}, context)
return result
except ZeroDivisionError:
return "错误: 除零"
except NameError as e:
return f"错误: {str(e)}"
except Exception as e:
return f"计算错误: {str(e)}"
def batch_calculate(self, formulas: Dict[str, str], **kwargs) -> Dict[str, Any]:
"""批量计算公式"""
results = {}
for name, formula in formulas.items():
results[name] = self.calculate(formula, **kwargs)
return results
# 使用示例
engine = FormulaEngine()
# 设置变量
engine.set_variable('price', 100)
engine.set_variable('tax_rate', 0.08)
# 添加自定义函数
def discount(price, rate):
return price * (1 - rate)
engine.add_function('discount', discount)
# 计算公式
formulas = {
'total_price': 'price * (1 + tax_rate)',
'discount_price': 'discount(price, 0.2)',
'monthly_payment': 'total_price / 12 + 10',
'complex': 'sqrt(price) + pow(tax_rate, 2)'
}
results = engine.batch_calculate(formulas)
for name, result in results.items():
print(f"{name}: {result}")
安全建议和最佳实践
完整的公式脚本系统
import ast
import operator
from typing import Any, Dict
class SafeFormulaEvaluator:
"""安全公式计算器"""
# 允许的操作符
OPERATORS = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Pow: operator.pow,
ast.USub: operator.neg,
ast.Mod: operator.mod
}
def __init__(self):
self.variables = {}
self.functions = {}
# 注册基本数学函数
self.register_math_functions()
def register_math_functions(self):
"""注册数学函数"""
import math
math_funcs = [
'abs', 'max', 'min', 'pow', 'sqrt',
'sin', 'cos', 'tan', 'log', 'log10',
'exp', 'ceil', 'floor', 'round'
]
for func_name in math_funcs:
if hasattr(math, func_name):
self.functions[func_name] = getattr(math, func_name)
elif func_name == 'abs':
self.functions[func_name] = abs
elif func_name in ('max', 'min'):
self.functions[func_name] = globals()[func_name]
def evaluate(self, expression: str, variables: Dict[str, Any] = None) -> Any:
"""安全评估表达式"""
if variables:
self.variables.update(variables)
try:
tree = ast.parse(expression, mode='eval')
return self._evaluate_node(tree.body)
except Exception as e:
raise ValueError(f"计算公式失败: {str(e)}")
def _evaluate_node(self, node):
"""评估AST节点"""
if isinstance(node, ast.Expression):
return self._evaluate_node(node.body)
elif isinstance(node, ast.Constant):
return node.value
elif isinstance(node, ast.Name):
if node.id in self.variables:
return self.variables[node.id]
elif node.id in self.functions:
return self.functions[node.id]
else:
raise NameError(f"未定义: {node.id}")
elif isinstance(node, ast.BinOp):
left = self._evaluate_node(node.left)
right = self._evaluate_node(node.right)
op = self.OPERATORS[type(node.op)]
return op(left, right)
elif isinstance(node, ast.UnaryOp):
operand = self._evaluate_node(node.operand)
op = self.OPERATORS[type(node.op)]
return op(operand)
elif isinstance(node, ast.Call):
func = self._evaluate_node(node.func)
args = [self._evaluate_node(arg) for arg in node.args]
if callable(func):
return func(*args)
raise TypeError(f"不可调用的对象: {func}")
elif isinstance(node, ast.List):
return [self._evaluate_node(item) for item in node.elts]
elif isinstance(node, ast.Tuple):
return tuple(self._evaluate_node(item) for item in node.elts)
else:
raise NotImplementedError(f"不支持的节点类型: {type(node).__name__}")
# 使用示例
evaluator = SafeFormulaEvaluator()
# 设置变量
variables = {
'x': 10,
'y': 20,
'z': 5
}
# 测试各种公式
test_formulas = [
"x + y",
"x * y - z",
"sqrt(x**2 + y**2)",
"sin(x) + cos(y)",
"max(x, y, z)",
"(x + y) / z + pow(x, 2)"
]
for formula in test_formulas:
try:
result = evaluator.evaluate(formula, variables)
print(f"{formula} = {result}")
except Exception as e:
print(f"错误: {formula} -> {str(e)}")
高级特性实现
条件表达式和循环
class AdvancedFormulaEngine:
"""高级公式引擎,支持条件和循环"""
def __init__(self):
self.variables = {}
self.functions = {}
self.condition_ops = {
'==': operator.eq,
'!=': operator.ne,
'<': operator.lt,
'>': operator.gt,
'<=': operator.le,
'>=': operator.ge,
'and': lambda x, y: x and y,
'or': lambda x, y: x or y,
'not': operator.not_
}
def evaluate(self, expression, variables=None):
if variables:
self.variables.update(variables)
return self._parse_and_execute(expression)
def _parse_and_execute(self, expr):
# 处理条件表达式
if ' if ' in expr and ' else ' in expr:
# 三元运算符
pattern = r'^(.+?)\s+if\s+(.+?)\s+else\s+(.+)$'
match = re.match(pattern, expr)
if match:
true_val = match.group(1)
condition = match.group(2)
false_val = match.group(3)
if self._parse_and_execute(condition):
return self._parse_and_execute(true_val)
else:
return self._parse_and_execute(false_val)
# 使用安全评估
return self._safe_eval(expr)
def _safe_eval(self, expression):
"""安全评估表达式"""
# 简化实现,实际应考虑所有安全因素
allowed_context = {
**self.variables,
**self.functions,
**self.condition_ops
}
try:
return eval(expression, {"__builtins__": {}}, allowed_context)
except Exception as e:
raise ValueError(f"计算错误: {str(e)}")
# 使用示例
engine = AdvancedFormulaEngine()
engine.variables = {'x': 15, 'y': 10, 'z': 8}
formula = "x if x > y else y"
result = engine.evaluate(formula)
print(f"条件结果: {result}") # 输出: 15
实现公式运算脚本的主要方法:
- 简单场景:使用
eval()或numexpr库 - 中等复杂度:自定义词法解析器
- 专业需求:使用
sympy、numexpr等专业库 - 完整系统:构建安全的公式引擎
选择建议:
- 快速原型:使用 Python 的
eval()(注意安全) - 生产环境:使用
numexpr或自定义解析器 - 科学计算:使用
sympy - 高性能需求:使用
numexpr或编译为字节码
记得始终考虑安全性,对用户输入的公式进行验证和限制。