Python短路函数列表

3

我有一个类,其中包含如下方法

class Validator:
    def _is_valid_code(self):
        return bool(#some logic here)

    def _is_valid_role(self):
        return bool(#some logic here)

    def _is_valid_age(self):
        return bool(#some logic here)
    .
    .
    .

如果它们全部为True,则我希望继续,否则返回一个字符串。
if not _is_valid_code():
    #short circuit and return a string
    return "not valid code"
if not _is_valid_role():
    #short circuit and return a string
    return "not valid role"
if not _is_valid_age():
    #short circuit and return a string with description
    return "not valid age"
#if we got here the request is valid so return a valid response
return valid_scenario

现在我对此没有任何问题,因为只有三个条件,但是,例如,如果我有更多的条件,比如说10个条件,我将最终得到很多if场景,所以我想知道是否可能像这样做,我只需要将条件逻辑和条件添加到列表中,然后就可以了。问题是当条件为false时如何获取字符串值。

conditions = [_is_valid_code(), _is_valid_role(), _is_valid_age()]
if all_conditions_true(conditions):
    return valid_scenario
else:
    return last_message_before_shortcut

还想知道这是否有效,是否可以被认为是符合Python风格并推荐的


创建方法/错误字符串对的列表。然后遍历该列表,调用方法,如果失败,则返回字符串。将其作为类变量,这样它实际上可以节省您大量时间。 - juanpa.arrivillaga
在出现错误的情况下,我不会返回一个字符串。每个函数要么返回一个有效的场景,要么引发异常。这样,如果你返回了任何东西,你就知道你有一个有效的场景。 - chepner
我忘了提到在所有情况下返回类型都是 json,实际上,当适用时,我会返回一个 HTTP 状态码和一个描述,有效场景与无效场景不同,因为 204 没有消息,字符串只是为了说明我想做什么,但我有几个403的情况,只有消息不同。 - jam
3个回答

3
你可以使用字典将函数映射到其对应的字符串上。
checks = {
    _is_valid_code: "not valid code",
    _is_valid_role: "not valid role",
    _is_valid_age: "not valid age",
    }

for f in checks:
    if not f():
        return checks[f]
return valid_scenario

请注意,在Python 3.7+中,字典会保留插入顺序。如果您使用的是旧版本或想更加严格地控制顺序,请使用OrderedDict或元组列表(包含函数和字符串)。
这本身是完全符合Python风格的,但我不知道上下文情况,所以无法确定在您的情况下是否适用。例如,如果valid_scenario不是字符串,我强烈建议使用异常代替混合返回类型。这个问题涵盖了这一点:为什么函数应始终返回相同的类型? 如果函数正在检查错误条件,我也建议采用相同的方法。

2
这样也可以,但我会把它变成元组列表而不是字典。因为你在这里没有使用任何字典特定的行为。 - Kirk Strauser
@Kirk 我在 checks[f] 中使用了查找。但是,是的,它不需要这样,这只是我思考的方式:如果函数失败,那么你才需要获取相关的字符串。你也可以使用 for f, s in checks.items() 或者等价的元组列表。 - wjandrea
1
这应该只是一对对的列表,但是正确的一般方法。 - juanpa.arrivillaga

2
我会做这样的事情:
def one():
    """string from one"""
    return False 

def two():
    """string from two"""
    return False 

def three():
    """string from three"""
    return True

def four():
    """string from four"""
    return False

conditions=[one, two, three, four]

接下来使用next方法,根据需要找到第一个TrueFalse,从函数中读取相关的文档字符串:

>>> print( next(f.__doc__ for f in conditions if f()) )  
string from three  

您可以使用带有next的默认值,表示所有内容都相同(根据情况为True或False):
next((f.__doc__ for f in conditions if f()), "All False")

或者,正如您表达的愿望一样:
def validator(conditions):
    return next((f.__doc__ for f in conditions if f()), None)

优点:

  1. 如果您在类中使用此方法,字符串会自动成为类范围内的变量(不会每次重新创建);

  2. [(one,"string from one"),(two,"string from two"),...] 更容易编辑(对我来说);

  3. 函数中的字符串和相关测试代码位于同一位置;

  4. 有条件地使用 next 提供了一个短路机制,并且会在第一个目标布尔值处停止执行;

  5. 条件列表 [one,two,three,...][(one,"string from one"),(two,"string from two"),...] 更容易看懂和理解;

  6. 还有一个!您可以完全跳过 __doc__ 字符串,只需使用描述性的函数名称以及:

    next((f.__name__ for f in conditions if f()), default)

  7. 如果您想要采用传统方法,也可以使用一组函数名和关联字符串的字典来使用 next


1
这种方法过于聪明了。只需使用一个方法和消息的元组列表即可。 - juanpa.arrivillaga
1
个人而言,我发现使用带有__doc__字符串的函数比使用元组列表更易于维护,因为每个测试结果和测试代码都在同一个地方。对我来说很有效! - dawg
1
我也使用 __doc__ 来做这些事情。我写下的答案最接近 OP 习惯的方式(也就是说,不要一次性改变太多)。在我的项目中,我绝对会使用 if not function(): return function.__doc__ - Kirk Strauser

2

我写了这些东西,比如:

for condition_is_ok, error_msg in [
    (_is_valid_code, "not valid code"),
    (_is_valid_role, "not valid role"),
    (_is_valid_age, "not valid age"),
]:
    if not condition_is_ok():
        return error_msg

或者,改变您的验证器工作方式:

  • 如果它们失败了,则返回一个解释原因的字符串。
  • 如果它们通过了,则返回 None。
class Validator:
    def _code_error(self):
        if ...:
            return "not valid code"
        return None

for checker in [_code_error, ...]:
    result = checker
    if result is not None:
        return result

1
这是正确的方法,但不要每次重新创建列表。它应该是一个类变量。 - juanpa.arrivillaga
当然可以!或者,如果您能够建立一个命名模式,您可以使用类似于 for function in dir(cls): if function.startswith('_validate_'): ... 的方式,在运行时检查类以收集所有定义的验证函数。为什么要重复自己?让Python帮助您变得懒惰。 - Kirk Strauser

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接