Is there a performance or code maintenance issue with using assert as part of the standard code instead of using it just for debugging purposes? Is assert x >= 0, 'x is less than zero' better or worse than if x < 0: raise Exception('x is less than zero') Also, is there any way to set a business rule like if x < 0 raise error that is always checked without the try/except/finally so, if at anytime throughout the code x is less than 0 an error is raised, like if you set assert x < 0 at the start of a function, anywhere within the function where x becomes less then 0 an exception is raised?
当前回答
如前所述,当您的代码should NOT到达某个点(意味着那里存在错误)时,应该使用断言。在我看来,使用断言最有用的原因可能是不变量/前置/后置条件。在循环或函数的每次迭代的开始或结束时,这些必须为真。
例如,一个递归函数(两个独立的函数,一个处理错误的输入,另一个处理错误的代码,因为它很难与递归区分开来)。如果我忘记写if语句,这将很明显地显示出哪里出了问题。
def SumToN(n):
if n <= 0:
raise ValueError, "N must be greater than or equal to 0"
else:
return RecursiveSum(n)
def RecursiveSum(n):
#precondition: n >= 0
assert(n >= 0)
if n == 0:
return 0
return RecursiveSum(n - 1) + n
#postcondition: returned sum of 1 to n
这些循环不变量通常可以用断言表示。
其他回答
"assert"语句在编译优化时被删除。所以,是的,它们在性能和功能上都有差异。
在编译时请求优化时,当前代码生成器不会为assert语句生成代码。Python 3 Docs
如果您使用assert来实现应用程序功能,然后优化部署到生产环境,那么您将受到“但它在开发中有效”缺陷的困扰。
参见PYTHONOPTIMIZE和-O -OO
英语单词assert在这里的意思是发誓、肯定、宣称。它的意思不是“检查”或“应该是”。这意味着你作为一个程序员要在这里做一个宣誓声明:
# I solemnly swear that here I will tell the truth, the whole truth,
# and nothing but the truth, under pains and penalties of perjury, so help me FSM
assert answer == 42
如果代码是正确的,除了单事件中断、硬件故障等,任何断言都不会失败。这就是为什么程序对终端用户的行为不能受到影响。特别是,断言即使在异常的编程条件下也不能失败。这种事从来没有发生过。如果发生这种情况,程序员应该为此受到惩罚。
assert的使用和异常的引发都与通信有关。
Assertions are statements about the correctness of code addressed at developers: An assertion in the code informs readers of the code about conditions that have to be fulfilled for the code being correct. An assertion that fails at run-time informs developers that there is a defect in the code that needs fixing. Exceptions are indications about non-typical situations that can occur at run-time but can not be resolved by the code at hand, addressed at the calling code to be handled there. The occurence of an exception does not indicate that there is a bug in the code.
最佳实践
因此,如果您将运行时发生的特定情况视为您想要通知开发人员的错误(“嗨,开发人员,这种情况表明某处存在错误,请修复代码。”),那么请使用断言。如果断言检查代码的输入参数,当输入参数违反条件时,您通常应该在文档中添加代码具有“未定义行为”。
相反,如果这种情况的出现并不是您眼中的错误,而是您认为应该由客户端代码处理的(可能很少,但可能)情况,则引发异常。引发哪种异常的情况应该是相应代码文档的一部分。
有没有演出[…]使用assert时的问题
断言的计算需要一些时间。但是,它们可以在编译时被删除。然而,这有一些后果,见下文。
有[…]使用assert的代码维护问题
Normally assertions improve the maintainability of the code, since they improve readability by making assumptions explicit and during run-time regularly verifying these assumptions. This will also help catching regressions. There is one issue, however, that needs to be kept in mind: Expressions used in assertions should have no side-effects. As mentioned above, assertions can be eliminated at compile time - which means that also the potential side-effects would disappear. This can - unintendedly - change the behaviour of the code.
我补充说,我经常使用断言来指定属性,比如循环不变量或我的代码应该具有的逻辑属性,就像我在正式验证的软件中指定它们一样。
它们有两个目的,告诉读者,帮助我推理,并检查我在推理中没有犯错误。例如 :
k = 0
for i in range(n):
assert k == i * (i + 1) // 2
k += i
#do some things
或者在更复杂的情况下:
def sorted(l):
return all(l1 <= l2 for l1, l2 in zip(l, l[1:]))
def mergesort(l):
if len(l) < 2: #python 3.10 will have match - case for this instead of checking length
return l
k = len(l // 2)
l1 = mergesort(l[:k])
l2 = mergesort(l[k:])
assert sorted(l1) # here the asserts allow me to explicit what properties my code should have
assert sorted(l2) # I expect them to be disabled in a production build
return merge(l1, l2)
因为当python在优化模式下运行时,断言是禁用的,所以不要犹豫在它们中编写代价高昂的条件,特别是当它使您的代码更清晰,更不容易出现错误时
是否存在性能问题?
Please remember to "make it work first before you make it work fast". Very few percent of any program are usually relevant for its speed. You can always kick out or simplify an assert if it ever proves to be a performance problem -- and most of them never will. Be pragmatic: Assume you have a method that processes a non-empty list of tuples and the program logic will break if those tuples are not immutable. You should write: def mymethod(listOfTuples): assert(all(type(tp)==tuple for tp in listOfTuples)) This is probably fine if your lists tend to be ten entries long, but it can become a problem if they have a million entries. But rather than discarding this valuable check entirely you could simply downgrade it to def mymethod(listOfTuples): assert(type(listOfTuples[0])==tuple) # in fact _all_ must be tuples! which is cheap but will likely catch most of the actual program errors anyway.