在开发企业级应用时,我们经常会遇到这样的场景:业务规则频繁变更,导致代码中充斥着大量if-else语句。每次业务调整都需要修改代码、重新部署,这不仅效率低下,还容易引入新的错误。这就是规则引擎要解决的问题。
Business_rules是一个轻量级的Python规则引擎库,它允许你将业务逻辑从代码中分离出来,实现规则的动态配置和管理。想象一下,当营销部门想调整促销策略时,不再需要开发人员介入,只需修改规则配置即可生效——这就是规则引擎的魅力所在。
我在电商平台项目中首次接触这个库时,系统中有超过200个业务条件判断。使用Business_rules重构后,业务人员可以通过YAML文件自行调整规则,我们的发布周期从每周一次延长到每月一次,而业务调整却更加灵活了。
Business_rules的架构围绕三个核心概念构建:
这种设计遵循了"观察-判断-执行"的模式,与人类决策过程高度相似。下面这张表展示了三者之间的关系:
| 组件 | 职责 | 类比现实场景 |
|---|---|---|
| 变量 | 提供决策依据 | 如库存数量、用户等级 |
| 规则 | 定义判断逻辑 | "如果库存<100且是VIP用户" |
| 动作 | 执行具体操作 | "发送补货通知并给予专属折扣" |
Business_rules内置了丰富的变量类型和操作符,这是它的核心能力所在:
python复制# 数值型变量示例
@numeric_rule_variable(label='库存数量')
def current_stock(self):
return self.product.stock
# 字符串型变量示例
@string_rule_variable()
def customer_level(self):
return self.user.level
每种变量类型都支持特定的操作符。例如数值型支持大小比较,字符串型支持正则匹配等。我在实际项目中发现,虽然内置类型已经覆盖大部分场景,但有时仍需要扩展:
python复制# 自定义列表类型变量
@select_rule_variable(options=['黄金', '白金', '钻石'])
def membership_type(self):
return self.user.membership
提示:设计变量时,label参数虽然可选,但我强烈建议添加。它会在规则配置界面显示更友好的名称,特别是有非技术人员参与规则配置时。
让我们通过一个电商促销案例来演示完整流程。首先定义变量类:
python复制from business_rules.variables import BaseVariables, numeric_rule_variable, string_rule_variable
from datetime import datetime
class PromotionVariables(BaseVariables):
def __init__(self, product, user):
self.product = product
self.user = user
@numeric_rule_variable(label='商品价格')
def product_price(self):
return self.product.price
@numeric_rule_variable(label='用户积分')
def user_points(self):
return self.user.loyalty_points
@string_rule_variable(label='用户等级')
def user_level(self):
return self.user.level
@numeric_rule_variable(label='购物车金额')
def cart_amount(self):
return sum(item.price for item in self.user.cart_items)
@string_rule_variable()
def current_season(self):
month = datetime.now().month
return 'winter' if month in [12,1,2] else 'summer' if month in [6,7,8] else 'other'
接下来定义可以执行的动作:
python复制from business_rules.actions import BaseActions, rule_action
from business_rules import fields
class PromotionActions(BaseActions):
def __init__(self, product, user):
self.product = product
self.user = user
@rule_action(params={
'discount_rate': fields.FIELD_NUMERIC,
'message': fields.FIELD_TEXT
})
def apply_discount(self, discount_rate, message):
original_price = self.product.price
self.product.price *= (1 - discount_rate)
print(f"{message} | 原价:{original_price}, 折后价:{self.product.price}")
@rule_action(params={
'points': fields.FIELD_NUMERIC
})
def add_loyalty_points(self, points):
self.user.loyalty_points += points
print(f"增加 {points} 积分 | 当前积分: {self.user.loyalty_points}")
@rule_action()
def free_shipping(self):
self.user.shipping_fee = 0
print("免运费已生效")
现在我们可以用YAML来定义业务规则了:
yaml复制- name: "VIP用户折扣"
conditions:
all:
- name: user_level
operator: equal_to
value: "VIP"
- name: cart_amount
operator: greater_than_or_equal_to
value: 500
actions:
- name: apply_discount
params:
discount_rate: 0.2
message: "VIP专属8折优惠"
- name: "冬季促销"
conditions:
any:
- name: current_season
operator: equal_to
value: "winter"
- name: product_price
operator: greater_than
value: 1000
all:
- name: user_points
operator: greater_than
value: 100
actions:
- name: free_shipping
- name: add_loyalty_points
params:
points: 50
最后是执行环节:
python复制from business_rules import run_all
def apply_promotions(product, user):
rules = load_rules_from_yaml('promotion_rules.yaml') # 从YAML加载规则
run_all(
rule_list=rules,
defined_variables=PromotionVariables(product, user),
defined_actions=PromotionActions(product, user),
stop_on_first_trigger=False
)
Business_rules支持嵌套的条件逻辑,这是它最强大的特性之一:
yaml复制conditions:
all: # 必须全部满足
- name: user_level
operator: equal_to
value: "VIP"
- any: # 满足任意一个
- name: cart_amount
operator: greater_than
value: 1000
- all: # 嵌套的all条件
- name: current_season
operator: equal_to
value: "winter"
- name: product_price
operator: less_than
value: 500
对于复杂的字符串匹配,可以使用matches_regex操作符:
yaml复制- name: "敏感地区限制"
conditions:
all:
- name: shipping_address
operator: matches_regex
value: "^(新疆|西藏|台湾).*"
actions:
- name: reject_order
params:
reason: "暂不支持该地区配送"
在大规模应用中,我总结了以下优化经验:
python复制# 变量缓存示例
@numeric_rule_variable()
def expensive_computation(self):
if not hasattr(self, '_cached_result'):
self._cached_result = do_heavy_computation()
return self._cached_result
当规则不按预期工作时,可以:
python复制# 调试装饰器示例
def log_rule_execution(func):
def wrapper(*args, **kwargs):
print(f"Evaluating rule: {func.__name__}")
result = func(*args, **kwargs)
print(f"Result: {result}")
return result
return wrapper
@log_rule_execution
@numeric_rule_variable()
def monitored_variable(self):
return self.value
当内置类型不满足需求时,可以扩展引擎:
python复制# 自定义类型示例
def datetime_rule_variable(func):
func.is_rule_variable = True
func.variable_type = 'datetime'
return func
在实际项目中,我建议:
python复制# 热加载示例
import threading
import time
import yaml
class RuleManager:
def __init__(self, rule_path):
self.rule_path = rule_path
self.rules = self._load_rules()
self._start_watcher()
def _load_rules(self):
with open(self.rule_path) as f:
return yaml.safe_load(f)
def _start_watcher(self):
def watch():
last_mtime = os.path.getmtime(self.rule_path)
while True:
time.sleep(5)
current_mtime = os.path.getmtime(self.rule_path)
if current_mtime > last_mtime:
self.rules = self._load_rules()
last_mtime = current_mtime
threading.Thread(target=watch, daemon=True).start()
虽然Business_rules非常实用,但Python生态中还有其他选择:
| 引擎 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Business_rules | 简单易用,YAML支持 | 类型系统有限 | 中小型项目 |
| Drools | 功能强大,生态完善 | 学习曲线陡峭 | 企业级复杂系统 |
| Pyke | 基于逻辑编程 | 文档较少 | 专家系统 |
| RuleMaster | 可视化界面 | 闭源商业软件 | 非技术用户 |
选择规则引擎时,需要考虑团队技能、项目规模和性能要求。对于大多数Python项目,Business_rules提供了最佳的平衡点。
在最近的一个电商平台项目中,我们使用Business_rules实现了完整的促销系统。以下是一些关键收获:
python复制# 文档生成示例
def generate_docs(variables_class, actions_class):
print("## 可用变量")
for name, func in vars(variables_class).items():
if hasattr(func, 'is_rule_variable'):
print(f"- {name}: {func.__doc__}")
print("\n## 可用动作")
for name, func in vars(actions_class).items():
if hasattr(func, 'is_rule_action'):
print(f"- {name}: {func.__doc__}")
Business_rules虽然小巧,但在合理的设计下完全可以支撑中等规模的业务规则管理。它的最大优势在于将业务逻辑的控制权交还给业务人员,让开发者能够专注于系统架构和性能优化。