应该使用断言来测试不应该发生的条件.目的是在程序状态损坏的情况下尽早崩溃.
异常应该用于可能发生的错误,并且您应该几乎总是创建自己的异常类.
例如,如果您正在编写一个函数来从配置文件读取到一个dict
,则文件中的格式不正确应该会引发一个ConfigurationSyntaxError
,而您可能assert
不会返回None
.
在您的示例中,if x
是通过用户界面或外部源设置的值,最好是异常.
如果x
仅在同一程序中由您自己的代码设置,请使用断言.
应该使用断言来测试不应该发生的条件.目的是在程序状态损坏的情况下尽早崩溃.
异常应该用于可能发生的错误,并且您应该几乎总是创建自己的异常类.
例如,如果您正在编写一个函数来从配置文件读取到一个dict
,则文件中的格式不正确应该会引发一个ConfigurationSyntaxError
,而您可能assert
不会返回None
.
在您的示例中,if x
是通过用户界面或外部源设置的值,最好是异常.
如果x
仅在同一程序中由您自己的代码设置,请使用断言.
优化编译时,将删除"assert"语句.所以,是的,有性能和功能差异.
在编译时请求优化时,当前代码生成器不会为assert语句发出任何代码.- Python 2.6.4 Docs
如果您使用assert
实现应用程序功能,然后优化部署到生产,您将受到"但它在工作中开发"缺陷的困扰.
参见PYTHONOPTIMIZE和-O -OO
当x在整个函数中变得小于零时能够自动抛出错误.您可以使用类描述符.这是一个例子:
class LessThanZeroException(Exception): pass class variable(object): def __init__(self, value=0): self.__x = value def __set__(self, obj, value): if value < 0: raise LessThanZeroException('x is less than zero') self.__x = value def __get__(self, obj, objType): return self.__x class MyClass(object): x = variable() >>> m = MyClass() >>> m.x = 10 >>> m.x -= 20 Traceback (most recent call last): File "", line 1, in File "my.py", line 7, in __set__ raise LessThanZeroException('x is less than zero') LessThanZeroException: x is less than zero
assert
假设您与四位同事Alice,Bernd,Carl和Daphne一起处理了200,000行代码.他们调用你的代码,你调用他们的代码.
然后assert
有四个角色:
告知Alice,Bernd,Carl和Daphne您的代码所期望的内容.
假设您有一个处理元组列表的方法,如果这些元组不是不可变的,程序逻辑可能会中断:
def mymethod(listOfTuples): assert(all(type(tp)==tuple for tp in listOfTuples))
这比文档中的等效信息更可靠,更易于维护.
通知计算机代码期望的内容.
assert
从代码的调用者强制执行适当的行为.如果你的代码调用Alices和Bernd的代码调用你的代码,那么没有assert
,如果程序在Alices代码崩溃,Bernd可能会认为这是Alice的错,Alice调查并可能认为这是你的错,你调查并告诉Bernd它实际上是他的.失去了很多工作.
有了断言,无论谁接到电话都是错误的,他们很快就会发现这是他们的错,而不是你的错.爱丽丝,伯恩德,你们都受益匪浅.节省了大量的时间.
告诉读者您的代码(包括您自己)在某些时候代码所取得的成就.
假设你有一个条目列表,每个条目都可以是干净的(这很好),或者它可以是smorsh,trale,gullup或者闪烁(这些都是不可接受的).如果它是smorsh它必须是unmorshed; 如果它是trale它必须是baludoed; 如果它是gullup它必须小跑(然后也可能节奏); 如果它闪烁,它必须再次闪烁,除了星期四.你明白这个想法:这很复杂.但最终结果是(或应该)所有条目都是干净的.Right Thing(TM)要做的是总结清洁循环的效果
assert(all(entry.isClean() for entry in mylist))
对于每个试图理解精彩循环实现的确切内容的人来说,这个陈述让人头疼.而这些人中最常见的可能就是你自己.
告知计算机您的代码在某些时候已经实现了什么.
你是否曾经忘记在小跑后调整需要它的条目,这assert
将节省你的一天,并避免你的代码打破亲爱的达芙妮的后期.
在我看来,assert
文档(1和3)和安全(2和4)的两个目的同样有价值.
告知人们甚至可能比告知计算机更有价值,因为它可以防止assert
目标捕获的错误(在案例1中)和任何情况下的大量后续错误.
除了其他答案之外,断言本身会抛出异常,但只会抛出AssertionErrors.从功利主义的角度来看,当你需要对你捕获的异常进行细粒度控制时,断言是不适合的.
这种方法唯一真正错误的是使用assert语句很难做出非常具描述性的异常.如果您正在寻找更简单的语法,请记住您也可以这样做:
class XLessThanZeroException(Exception): pass def CheckX(x): if x < 0: raise XLessThanZeroException() def foo(x): CheckX(x) #do stuff here
另一个问题是使用assert进行正常的条件检查是因为它很难使用-O标志禁用调试断言.
英语语言文字断言在这里的意义上使用发誓,肯定,出来的事.它并不意味着"检查"或"应该".这意味着你作为一名程序员在这里发表了宣誓声明:
# 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
如果代码是正确的,除了单事件干扰,硬件故障等,没有断言将失败.这就是为什么程序对最终用户的行为不得受到影响的原因.特别是,即使在特殊的程序条件下,断言也不会失败.它永远不会发生.如果它发生了,那么程序员应该为之奋斗.
如前所述,当您的代码不应该达到某一点时,应该使用断言,这意味着存在错误.我可以看到使用断言的最有用的原因可能是一个不变/前/后条件.这些在循环或函数的每次迭代的开始或结束时必须是真的.
例如,一个递归函数(2个单独的函数,因此1处理错误的输入,另一个处理错误的代码,导致很难用递归区分).如果我忘记编写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
这些循环不变量通常可以用断言表示.