Comparison in Python assert statement
在检查了几个python开源项目之后,我找到了相同函数具有assert <>和assert <> is True的测试用例的示例。
而且我无法找到有关使用assert语句的最佳实践的答案,无论是否有完整条件。
例如功能
1 2 3 4 5
| def is_even(number):
if number % 2 == 0:
return True
else:
return False |
可以这样测试
1 2
| assert is_even(6) is True
assert is_even(6) == True |
但它也可以测试
如果函数is_even将被更改,我在这里看到问题原因
1 2 3 4 5
| def is_even(number):
if number % 2 == 0:
return <>
else:
return False |
其中<>可以在任何数字(0除外)或非空字符串上更改 - 最后一次测试将通过。 但这将是另一个功能。
我看到了这个以及关于断言用法的问题,但似乎它们并没有完全覆盖我的问题。
另一方面,PEP 8规范有这样的例子
但是它也是关于assert的用法吗?
-
是的,关于布尔值的PEP-8建议也适用于assert用法。同样,该函数应写为:def is_even(number): return number % 2 == 0
-
你究竟在问什么?
-
@DeepSpace问题是关于使用测试的好习惯assert is_even(6)或者更好地使用assert is_even(6) is True导致函数可以更改而assert也会传递,但这将是另一个函数(将返回另一个结果)
-
如果你的意思是True,为什么你会返回字符串'True'而不是返回True?如果您使用assert来检查函数是否返回了预期的字符串,那么PEP8关于布尔值的建议是不相关的。
-
好的,做assert is_even(6) is True和assert is_even(5) is False是合理的,因为你想确保is_even返回一个布尔值。 OTOH,函数名称暗示它返回一个布尔值,任何调用它的人都应该跟随PEP-8,并调用它像if is_even(n):,而不是检查它返回的实际值。
-
@TBurgis它只是True和True的例子。也许不是最好的。但感谢您对PEP8关于布尔值的建议的意见!
-
另请注意,assert经常被误用,它应该只用于健全性检查(应该是不可能的事情),你应该raise除了输入数据之外的其他任何异常
-
PEP-8对此非常清楚:不要使用==将布尔值与True或False进行比较。无论你在何处执行此操作都无关紧要;示例使用if,但这无关紧要。断言适用于任何上下文。对于if,assert或while,条件表达式或作为独立表达式进行测试无关紧要,其结果存储在变量中。
代码就像
1 2 3 4
| if a:
return True
else:
return False |
反正是不洁净的。它应该写成return a,或者,如果需要bool(而不仅仅是一个条件值),return bool(a)。
如果你使用v == True或v is True完全符合这种情况。第一个将检查值是否等于值True。值是否为此,也取决于此值的类(1 == True为true,1.0 == True为true,但1j == True为false)。
如果你使用is进行比较,那么你绝对清楚地表明你接受了值True而没有别的。
但是,在大多数情况下,我认为仅仅声明assert f(x)将是语义上正确的事情。例如,对于像assert is_in_rectangle(point)这样的断言。将其拼写为assert is_in_rectangle(point) == True或assert is_in_rectangle(point) is True将是错误的,因为任何不完整的值(例如[ 1 ])都应该足够了。检查显式True作为结果将假设太多。
所以,基线:不要将truish值与True进行比较。如果您对值有非常具体的要求,例如"必须是一个bool,而不仅仅是truish"(这是一种罕见的情况),则仅将结果值与True进行比较。
assert与if完全相同。它是一个关键字,后跟一个布尔表达式,它要求表达式求值为True。当且仅当if expression成功时,语句assert expression才会成功。
鉴于此,您的问题有两个方面。第一个condition vs condition==True。这是一种风格问题。你不写if condition==True:,只是if condition:。同样,没有理由写
1
| assert is_even(6) == True |
写吧
代替。
第二个是condition==True vs condition is True。这不仅仅是风格问题。运行以下
1 2 3 4 5 6 7 8
| if []==False: # also: if not []
print('false')
if bool([]) is False:
print('false')
if [] is False:
print('false') |
将打印示例(1)和(2),但示例(3)将不打印。
False是单例值,这意味着python解释器中只有一个False对象,并且所有虚假对象都是同一个对象。因此,使用is感觉正确(就像它被认为是写if object is None的好风格)。但是,python中存在falsy / truthy值的概念:如果condition为false,则if condition不一定触发,但如果bool(condition)为false。这是自动完成的。在上面的示例中,空列表是一个虚假值。即bool([])评估False对象(再次,只有一个)。因此,示例(1)成功,因为它自动转换(内部)类似于示例(2)。但是,示例(3)不起作用,因为尽管空列表的计算结果为false,但它不是False。这是一个空列表。所以,总结你的例子
1 2 3
| if greeting: # good style, and it works
if greeting==True: # bad style, redundant, but it still works
if greeting is True: # misleading. does a different thing than it seems to do. avoid it |
结束评论:你举例说明
1 2 3 4 5
| def is_even(number):
if number % 2 == 0:
return <>
else:
return False |
你提到<>可能是任何真正的价值。本着这种精神,它等同于写作
1 2
| def is_even(number):
return number % 2 |
因为它会返回非零(真实)或零(虚假)。但是,不要这样做。名为is_even的函数的用户希望函数返回True或False,而不是整数。您不知道该用户如何使用您的代码(例如,可能将其发送到数据库,或者需要真正布尔值的Web服务)。所以最好返回一个布尔值。因此,函数的最短和最安全的形式是写
1 2
| def is_even(number):
return bool(number % 2) |