Python断言的最佳实践
-
使用
assert
作为标准代码的一部分,而不是仅仅为了debugging目的而使用它,是否存在性能或代码维护问题?是
assert x >= 0, 'x is less than zero'
好于或差于
if x < 0: raise Exception, 'x is less than zero'
-
另外,是否有任何方法来设置一个业务规则,
if x < 0 raise error
,总是检查没有try/except/finally
,如果在整个代码x
任何时候x
小于0的错误引发,就像你在函数开始处设置assert x < 0
,函数中x
变小于0的任何地方都会引发exception?
在整个函数中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() >>> mx = 10 >>> mx -= 20 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "my.py", line 7, in __set__ raise LessThanZeroException('x is less than zero') LessThanZeroException: x is less than zero
应该使用断言来testing永远不会发生的情况 。 目的是在腐败程序状态的情况下尽早崩溃。
应该使用exception来处理可能发生的错误,并且几乎总是要创build自己的Exception类。
例如,如果你正在编写一个从configuration文件读入dict
的函数,那么文件中的不正确的格式化会引发一个ConfigurationSyntaxError
,而你可以assert
你不会返回None
。
在您的示例中,如果x
是通过用户界面或外部源设置的值,则最好是exception。
如果x
只是由你自己的代码在同一个程序中设置的,那就去一个断言。
编译优化时,“断言”语句将被删除 。 所以,是的,性能和function上都有差异。
当编译时请求优化时,当前的代码生成器不会发出assert语句的代码。 – Python 2.6.4文档
如果使用assert
来实现应用程序function,然后优化部署到生产,那么您将受到“工作在设备中”缺陷的困扰。
见PYTHONOPTIMIZE和-O -OO
假设你和四个同事Alice,Bernd,Carl和Daphne一起工作了20万行代码。 他们称你的代码,你打电话给他们的代码。
然后assert
有四个angular色 :
-
告诉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它在其实他的。 失去了大量的工作。
有了断言,谁打错电话,他们很快就能看出这是他们的错,而不是你们的错。 爱丽丝,贝恩德,你们都受益匪浅。 节省大量的时间。 -
告诉读者你的代码(包括你自己)在某些时候你的代码已经达到了什么程度。
假设你有一个条目列表,并且每个条目都可以是干净的(这是好的),也可以是干净的,trale,gullup或闪烁的(这些都是不可接受的)。 如果是肮脏的,那就一定不要装饰; 如果它是trale它必须是baludoed; 如果是海鸥,它必须被小跑(然后也可能起步)。 如果闪烁,除周四外,必须再次闪烁。 你明白了:这是复杂的东西。 但最终的结果是(或应该是)所有条目都是干净的。 正确的事情(TM)要做的是总结清洁循环的影响assert(all(entry.isClean() for entry in mylist))
这个陈述让每个人都很头痛,因为他们试图了解美妙的循环到底是什么。 而这些人中最常见的可能是你自己。
-
告诉计算机你的代码在某些时候已经达到了什么程度。
如果你忘记在小跑之后join需要的条目,这个assert
将会节省你的一天,并且避免你的代码在达芙妮的晚些时候毁了。
在我看来, assert
文档(1和3)和保护(2和4)这两个目的同样重要。
告知人们甚至比通知计算机更有价值,因为它可以防止assert
要纠正的错误(情况1)以及随时发生的大量错误。
除了其他的答案,断言自己会抛出exception,但只有断言错误。 从功利的angular度来看,断言并不适合于需要细粒度控制的情况。
这种方法唯一的错误是,使用断言语句很难做出非常具有描述性的exception。 如果你正在寻找更简单的语法,记住你也可以做这样的事情:
class XLessThanZeroException(Exception): pass def CheckX(x): if x < 0: raise XLessThanZeroException() def foo(x): CheckX(x) #do stuff here
另一个问题是,使用assert进行正常的条件检查是因为使用-O标志禁止debugging断言很困难。
如前所述,当你的代码不应该达到一个点时,应该使用断言,这意味着那里有一个错误。 我可以看到使用断言的最有用的原因可能是一个不变的/前/后置的条件。 在循环或函数的每次迭代的开始或结束时,这些都必须是真实的。
例如,一个recursion函数(2个单独的函数,所以1处理错误的input,另一个处理错误的代码,导致很难区分recursion)。 如果我忘记写下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
这些循环不variables通常可以用断言来表示。
至于“ 是否有性能问题 ?”:
-
请记住“先让它工作,然后再让它快速工作” 。
任何程序的百分比通常与其速度相关。 如果它永远certificate是一个性能问题,你总是可以踢出或简化一个assert
– 而且绝大多数永远不会。 -
务实:
假设你有一个处理元组的非空列表的方法,如果这些元组不是不可变的,程序逻辑将会中断。 你应该写:def mymethod(listOfTuples): assert(all(type(tp)==tuple for tp in listOfTuples))
如果你的名单往往是十个条目,这可能是好的,但如果他们有一百万个条目,这可能会成为一个问题。 但是,不要完全丢弃这个有价值的检查,你可以简单地降级它
def mymethod(listOfTuples): assert(type(listOfTuples[0])==tuple) # in fact _all_ must be tuples!
这是便宜,但可能会赶上97%的实际程序错误。
在像PTVS这样的IDE中,PyCharm,Wing assert isinstance()
语句可以用来为一些不清楚的对象启用代码完成。
有一个名为JBoss Drools的java框架,它执行运行时监视来声明业务规则,这将回答你的问题的第二部分。 不过,我不确定python是否有这样的框架。
断言是检查 –
1.有效的条件,
2.有效声明,
真正的逻辑
的源代码。 不是整个项目失败,而是在源文件中发出一些不适合的警告。
在例1中,因为variables'str'不是nul。 所以没有任何断言或exception提出。
例1:
#!/usr/bin/python str = 'hello Pyhton!' strNull = 'string is Null' if __debug__: if not str: raise AssertionError(strNull) print str if __debug__: print 'FileName '.ljust(30,'.'),(__name__) print 'FilePath '.ljust(30,'.'),(__file__) ------------------------------------------------------ Output: hello Pyhton! FileName ..................... hello FilePath ..................... C:/Python\hello.py
在例子2中,var'str'是nul。 所以我们通过assert语句来保存用户从错误的程序之前。
例2:
#!/usr/bin/python str = '' strNull = 'NULL String' if __debug__: if not str: raise AssertionError(strNull) print str if __debug__: print 'FileName '.ljust(30,'.'),(__name__) print 'FilePath '.ljust(30,'.'),(__file__) ------------------------------------------------------ Output: AssertionError: NULL String
我们不想debugging的时候,就意识到了源代码中的断言问题。 禁用优化标志
python -O assertStatement.py
没有什么会得到打印
这里所说的英语单词是用来发誓 , 肯定 , 公开的 。 这并不意味着“检查”或“应该” 。 这意味着你作为一个编码器在这里发表宣誓声明 :
# 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
如果代码是正确的,禁止单事件翻倒 ,硬件故障等, 没有断言将永远失败 。 这就是为什么程序对最终用户的行为不能受到影响。 特别是,即使在特殊的编程条件下,断言也不会失败。 它从来没有发生过。 如果发生了,程序员应该被激活