如何防止短路评估?

29

这是我在处理一个Django项目时遇到的问题,涉及到表单验证。

在Django中,当您有一个提交的表单时,可以在对应的表单对象上调用is_valid()来触发验证并返回布尔值。因此,通常您会在视图函数中编写类似下面的代码:

if form.is_valid():
    # code to save the form data

is_valid()不仅验证表单数据,还会向表单对象添加错误消息,以便随后显示给用户。

在一个页面上,我同时使用两个表单,并且只有在这两个表单都包含有效数据时才希望保存数据。这意味着在执行保存数据的代码之前必须对这两个表单都调用is_valid()。最明显的方法是:

if form1.is_valid() and form2.is_valid():
    # ...

由于逻辑运算符的短路求值,这种方法行不通。如果form1无效,则form2将不被评估,因此其错误消息将丢失。

这只是一个例子。据我所知,在其他语言(如Smalltalk)中,没有贪婪的and/or替代方案。我可以想象在不同情况下(而不仅仅是在Python中)出现这样的问题。我能想到的解决方案都有些笨拙(嵌套的if语句、将返回值分配给本地变量并在if语句中使用它们)。我想知道解决这种问题的Python方式。

3个回答

40

可以这样做:

if all([form1.is_valid(), form2.is_valid()]):
   ...

一般情况下,可以使用列表推导式来预先计算结果(与生成器表达式相反,后者在此上下文中通常被使用)。例如:

if all([ form.is_valid() for form in (form1,form2) ])  

这个方法可以很好地扩展到任意数量的条件...唯一的限制是它们都需要通过"and"连接,而不是像 if foo and bar or baz: ... 那样使用"or"。

(对于一个非短路的or,你可以使用any而不是all)。


2
我用了几秒钟想到了这个。这是一个我之前没有考虑过的边角情况(我经常使用不保证短路计算但允许的Fortran进行工作),我总是试图弄清楚如何确保我的表达式被短路。对我来说,找出这个有点反常:)。 - mgilson
是的,all 是这里的最佳选择,但你在哪里使用了列表推导式?我只看到了一个简单的列表示例。 - rantanplan
@rantanplan -- 你说得对。 (哎呀)。 我在考虑一般情况下可以使用列表推导式。 我会修复它的。 - mgilson
还要注意,上述表单可以与@BigYellowCactus提供的解决方案相结合,因为anyall返回布尔值。 - mgilson

27

你可以简单地使用二进制位运算符&,它将对bool值执行非短路逻辑AND操作。

if form1.is_valid() & form2.is_valid():
   ...

6
更具体地说,它将对整数执行按位“与”操作。 由于布尔值恰好是从具有“True == 1”和“False == 0”的整数派生而来的,因此这种方法有效。 它不适用于其他类型或返回非布尔值的函数(不一定能用)。 尽管如此,它是一个很好的工具(+1)。 - mgilson
1
绝对比mgilson的解决方案简单。感谢!另一个可能对阅读代码的其他人更有帮助。在这里,我想你可以认为我只是混淆了and& - j0ker
6
相较于我的解决方案,一个优势是你不仅仅局限于使用and当所有的结果都是布尔类型(True,False,10)。你可以写出这样的表达式 if foo() & baz() | bar() ,它将按预期行事。 - mgilson
5
与其他评论所说的相反,这个回答完全正确。事实上,这是布尔值的特定设计和预期行为。 bool 甚至有其自己的 bool.__and__,对于 True & True 返回 True 而不是 1,对于其他按位运算符也是如此,因此它不仅继承了整数实现(尽管它与整数行为兼容)。 - user2357112
2
这里有一个潜在的陷阱,all 没有这个问题 - 它只在返回值实际上是布尔值时才起作用,它不会测试非布尔值的“真实性”。例如,all([1, 2])True,因为 12 都是真实的,但 1 & 2 是虚假的。在其他一些情况下,& 将引发 TypeError - kaya3
显示剩余2条评论

1

你可以使用中缀运算符(ActiveState Python recipe)来定义自己的布尔运算符:

aand = Infix(lambda x,y: bool(x) and bool(y))
1 |aand| 2  # Will return `True` instead of `1`

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