在 Python 中更清晰地表示规则 (if-else)
Cleaner way to represent Rules (if-else) in Python
我正在尝试寻找一种设计模式(或 maybe an algorithm)来帮助我以更清晰的方式编写这些规则。有什么建议吗?
def get_rules(user, value):
if 500 <= value < 5000 and not user.address:
return [REQUEST_ADDRESS]
if value >= 5000:
if not user.address and not user.phone:
return [REQUEST_ADDRESS, REQUEST_PHONE]
if user.address and not user.phone:
return [REQUEST_PHONE]
if not user.address and user.phone:
return [REQUEST_ADDRESS]
# Potentially ~20 more conditions here based on various attributes of user
return [STATES.REQUEST_NONE]
注意:我不是在寻找规则引擎,因为我不想通过在 python 中添加 "business friendly" DSL 来使我的代码复杂化。 Python 本身就是一种简单的语言来编写这些规则。
有趣的阅读:http://martinfowler.com/bliki/RulesEngine.html(但我仍在努力远离 "framework" 为我做这件事)。
在这种情况下你可以使用字典:
resdict = {(False, False): [REQUEST_ADDRESS, REQUEST_PHONE],
(True, False): [REQUEST_PHONE],
(False, True): [REQUEST_ADDRESS]}
return resdict[(user.address, user.phone)]
您还可以使用列表理解:
return [req for req, haveit in zip([REQUEST_ADDRESS, REQUEST_PHONE], [user.address, user.phone]) if not haveit]
或更简单的列表追加:
res = []
if not user.address:
res.append(REQUEST_ADDRESS)
if not user.phone:
res.append(REQUEST_PHONE)
您正在使用 "if a and not b else check not a and b else check not a and not b" 策略检查许多不同的组合,以确定您需要发送哪些请求组合。
相反,只检查您遗漏的内容:
missing = []
if not user.phone:
missing.append(REQUEST_PHONE)
if not user.address:
missing.append(REQUEST_ADDRESS)
return missing or [REQUEST_NONE]
如果我没理解错的话,你就有了用户的属性列表。如果一个为假,则应将 REQUEST
值添加到列表中。那么这可能会有所帮助:
# define all your combinations here:
mapping = {'address': REQUEST_ADDRESS, 'phone': REQUEST_PHONE, …)
return [value for key, value in mapping.items()
if not getattr(user, key, None)]
看起来您的 "rules" 可以归结为:为对象 user
中不作为属性存在的字段请求值。我假设属性到请求的映射可以是任意的;您可以将其表示为字典映射,例如像这样:
rulemap = {
"address": REQUEST_ADDRESS,
"phone": REQUEST_PHONE,
# etc.
}
然后,您可以通过检查 rulemap
中的哪些键未作为对象 user
:
中的属性来获取要发出的请求列表
return [ rulemap[fld] for fld in rulemap.keys() if fld not in user.__dict__ ]
我正在尝试寻找一种设计模式(或 maybe an algorithm)来帮助我以更清晰的方式编写这些规则。有什么建议吗?
def get_rules(user, value):
if 500 <= value < 5000 and not user.address:
return [REQUEST_ADDRESS]
if value >= 5000:
if not user.address and not user.phone:
return [REQUEST_ADDRESS, REQUEST_PHONE]
if user.address and not user.phone:
return [REQUEST_PHONE]
if not user.address and user.phone:
return [REQUEST_ADDRESS]
# Potentially ~20 more conditions here based on various attributes of user
return [STATES.REQUEST_NONE]
注意:我不是在寻找规则引擎,因为我不想通过在 python 中添加 "business friendly" DSL 来使我的代码复杂化。 Python 本身就是一种简单的语言来编写这些规则。
有趣的阅读:http://martinfowler.com/bliki/RulesEngine.html(但我仍在努力远离 "framework" 为我做这件事)。
在这种情况下你可以使用字典:
resdict = {(False, False): [REQUEST_ADDRESS, REQUEST_PHONE],
(True, False): [REQUEST_PHONE],
(False, True): [REQUEST_ADDRESS]}
return resdict[(user.address, user.phone)]
您还可以使用列表理解:
return [req for req, haveit in zip([REQUEST_ADDRESS, REQUEST_PHONE], [user.address, user.phone]) if not haveit]
或更简单的列表追加:
res = []
if not user.address:
res.append(REQUEST_ADDRESS)
if not user.phone:
res.append(REQUEST_PHONE)
您正在使用 "if a and not b else check not a and b else check not a and not b" 策略检查许多不同的组合,以确定您需要发送哪些请求组合。
相反,只检查您遗漏的内容:
missing = []
if not user.phone:
missing.append(REQUEST_PHONE)
if not user.address:
missing.append(REQUEST_ADDRESS)
return missing or [REQUEST_NONE]
如果我没理解错的话,你就有了用户的属性列表。如果一个为假,则应将 REQUEST
值添加到列表中。那么这可能会有所帮助:
# define all your combinations here:
mapping = {'address': REQUEST_ADDRESS, 'phone': REQUEST_PHONE, …)
return [value for key, value in mapping.items()
if not getattr(user, key, None)]
看起来您的 "rules" 可以归结为:为对象 user
中不作为属性存在的字段请求值。我假设属性到请求的映射可以是任意的;您可以将其表示为字典映射,例如像这样:
rulemap = {
"address": REQUEST_ADDRESS,
"phone": REQUEST_PHONE,
# etc.
}
然后,您可以通过检查 rulemap
中的哪些键未作为对象 user
:
return [ rulemap[fld] for fld in rulemap.keys() if fld not in user.__dict__ ]