我最近的一个编码错误让我觉得......
我一直在我的一个函数中使用assert false
而不是assert False
。
此函数仅在try/except
子句中调用。
所以我从来没有注意到这个"编译错误",直到我实际打印了异常的细节。
然后这让我想知道两者之间是否有任何运行时差异。
当然,这里的"false
"可以用任何其他未定义的符号代替。
显然,打印输出本身会有所不同。
这是我进行的一个简单的测试:
try:
assert false
except Exception,e:
print "false: class name = {:15}, message = {}".format(e.__class__.__name__,e.message)
try:
assert False
except Exception,e:
print "False: class name = {:15}, message = {}".format(e.__class__.__name__,e.message)
此测试的打印输出为:
false: class name = NameError , message = name 'false' is not defined
False: class name = AssertionError , message =
所以我的问题是,这里还有其他运行时差异吗?特别是,我很想知道在assert(False)
上使用assert(false)
是否会以某种方式阻碍我的程序的性能。
这两个版本都是错误的,永远不应该使用。
-
assert false, ...
:永远不会到达assert
语句,因为false
表达式会引发NameError
异常。这闻起来像是你的代码中的错误,而不是代表你的故意行为。切勿使用故意错误来引发异常。你必须添加一个注释,解释你为什么这样做给未来的代码维护者,但永远不应该有理由使用它,因为有更好的替代方案。
-
assert False, ...
:这是一个故意的断言失败,看起来像是调试而不是生产代码的尝试。在断言不成立时可能会失败的代码之前进行断言。如果需要在此时让异常退出代码,请引发异常。
要明确。引发异常。即使提出AssertionError
异常也更好:
raise AssertionError('This should never be reached; boundary checks failed')
从你永远不应该使用的两个版本中,在Python 3中assert(False, ...)
"更快",因为至少不会触发全局名称搜索。这是因为在 Python 3 中,False
是一个关键字,因此编译器可以通过引用常量来优化它。但是,两者之间几乎没有实际区别。由于故意失败的断言在设计上永远不会达到,或者最多达到一次,因此担心它们的表现是一个有争议的问题。
这不是编译错误,两者都是运行时错误,显然存在差异:
评估false
会产生 NameError,这意味着您引用了一个未定义的名称。另一个代码路径可能会导致定义名称,在这种情况下,断言可能不会失败。这个微不足道的例子是它上面的一个false = True
语句(这显然在另一个方面是可怕的,因为变量名如此糟糕(。无论哪种方式,未定义名称的代码路径都可以到达表达式,这是一个逻辑错误。
断言False
是一种强制抛出AssertionError
的方法。这是一种非常糟糕的技术,因为您添加了显式代码以失败,而没有任何解释原因;我们无法解释错误,必须在代码中找到它才能找出它发生的原因。断言应同时具有逻辑表达式和说明,以帮助调试它们。
无论哪种方式,您处理任何异常的方式都会丢失大量信息。特别是,有关try
内发生错误的位置的任何信息。正常的异常回溯(可以使用 traceback.print_exception 及其同类访问(详细包含此信息。
实际上,这种处理assert
和try
-catch
的样式会阻止您接收有关任何错误的有用信息,但编译时错误(如 SyntaxError 和 ImportError(除外。
我能想到的唯一明智的assert False
用途是测试assert
语句本身。在所有正常使用中,我们都包括故障条件。