Python断言的最佳实践

  1. 使用assert作为标准代码的一部分,而不是仅仅为了debugging目的而使用它,是否存在性能或代码维护问题?

     assert x >= 0, 'x is less than zero' 

    好于或差于

     if x < 0: raise Exception, 'x is less than zero' 
  2. 另外,是否有任何方法来设置一个业务规则, 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色

  1. 告诉Alice,Bernd,Carl和Daphne你的代码所期望的。
    假设你有一个处理元组列表的方法,如果这些元组不是不可变的,程序逻辑就会中断:

     def mymethod(listOfTuples): assert(all(type(tp)==tuple for tp in listOfTuples)) 

    这比文档中的等价信息更值得信赖,而且更容易维护。

  2. 告诉计算机你的代码需要什么。
    assert从您的代码的调用者执行适当的行为。 如果你的代码调用Alices和Bernd的代码调用你的,那么没有assert ,如果程序在Alices代码中崩溃,Bernd可能认为这是Alice的错,Alice调查并可能认为这是你的错,你调查并告诉Bernd它在其实他的。 失去了大量的工作。
    有了断言,谁打错电话,他们很快就能看出这是他们的错,而不是你们的错。 爱丽丝,贝恩德,你们都受益匪浅。 节省大量的时间。

  3. 告诉读者你的代码(包括你自己)在某些时候你的代码已经达到了什么程度。
    假设你有一个条目列表,并且每个条目都可以是干净的(这是好的),也可以是干净的,trale,gullup或闪烁的(这些都是不可接受的)。 如果是肮脏的,那就一定不要装饰; 如果它是trale它必须是baludoed; 如果是海鸥,它必须被小跑(然后也可能起步)。 如果闪烁,除周四外,必须再次闪烁。 你明白了:这是复杂的东西。 但最终的结果是(或应该是)所有条目都是干净的。 正确的事情(TM)要做的是总结清洁循环的影响

     assert(all(entry.isClean() for entry in mylist)) 

    这个陈述让每个人都很头痛,因为他们试图了解美妙的循环到底是什么。 而这些人中最常见的可能是你自己。

  4. 告诉计算机你的代码在某些时候已经达到了什么程度。
    如果你忘记在小跑之后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 

如果代码是正确的,禁止单事件翻倒 ,硬件故障等, 没有断言将永远失败 。 这就是为什么程序对最终用户的行为不能受到影响。 特别是,即使在特殊的编程条件下,断言也不会失败。 它从来没有发生过。 如果发生了,程序员应该被激活

Interesting Posts