【问题标题】:Cleaner way to represent Rules (if-else) in Python在 Python 中表示规则(if-else)的更简洁的方法
【发布时间】:2015-06-05 09:46:59
【问题描述】:

我正在尝试找到一种设计模式(或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 中添加“业务友好”的 DSL 来使我的代码复杂化。 Python 本身就是一种编写这些规则的简单语言。

有趣的阅读:http://martinfowler.com/bliki/RulesEngine.html(但我仍然试图远离“框架”来为我做这件事)。

【问题讨论】:

  • 我的错,更新了sn-p。 amount 什么都不是,它只是 value,它可以是一个变量,我需要根据它做出决定。
  • 为什么不建立一个列表并将您需要请求的值添加到该列表中?这消除了很多情况。
  • 当然,我会这样做的。但这不会减少 if-else 检查。
  • 肯定会,因为你不需要检查'if a and not b, if not a and not b',而是'if not a, if not b,如果不是 c'。您将拥有n 案例,而不是所有这些案例组合。
  • 我明白你的建议,这看起来更干净我希望有一个解决方案可能使用多态性来摆脱 if-else。可能是我想多了。

标签: python business-rules


【解决方案1】:

看起来您的“规则”归结为:请求对象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__ ]

【讨论】:

    【解决方案2】:

    如果我对问题的理解正确,您就有了用户的属性列表。如果其中一个为假,则应将 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)]
    

    【讨论】:

      【解决方案3】:

      在这种情况下你可以使用字典:

      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)
      

      【讨论】:

      • 我认为这不是很好。
      • 是的,如果我必须检查 2 个条件,1 个使用 user.address 和 user.phone,另一个使用 user.age,该怎么办?
      • resdict 的复杂度为 O(2^n)
      • 列表理解是线性的,但为了便于阅读,我会选择我的建议/您的最后一个选项。
      【解决方案4】:

      您正在使用“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]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-24
        • 1970-01-01
        • 2014-10-02
        • 2021-09-29
        • 2013-06-26
        相关资源
        最近更新 更多