Python断言的最佳实践

巧了我就是萌 提交于 2020-02-27 19:39:58
  1. 使用assert作为标准代码的一部分而不是仅仅用于调试目的,是否存在性能或代码维护问题?

    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任何时候小于0都会引发错误,就像你在函数开始时设置assert x < 0 ,函数中x变得小于0的任何地方都会引发异常?


#1楼

如前所述,当您的代码不应该达到某一点时,应该使用断言,这意味着存在错误。 我可以看到使用断言的最有用的原因可能是一个不变/前/后条件。 这些在循环或函数的每次迭代的开始或结束时必须是真的。

例如,一个递归函数(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

这些循环不变量通常可以用断言表示。


#2楼

优化编译时,将删除“assert”语句 。 所以,是的,有性能和功能差异。

在编译时请求优化时,当前代码生成器不会为assert语句发出任何代码。 - Python 2.6.4 Docs

如果您使用assert来实现应用程序功能,然后将部署优化到生产,您将会受到“但它在工作中开发”缺陷的困扰。

参见PYTHONOPTIMIZE-O -​​OO


#3楼

除了其他答案之外,断言本身会抛出异常,但只会抛出AssertionErrors。 从功利主义的角度来看,当你需要对你捕获的异常进行细粒度控制时,断言是不适合的。


#4楼

assert的四个目的

假设您与四位同事Alice,Bernd,Carl和Daphne一起处理了200,000行代码。 他们调用你的代码,你调用他们的代码。

然后assert四个角色

  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. 告诉读者您的代码(包括您自己)在某些时候代码所取得的成就。
    假设你有一个条目列表,每个条目都可以是干净的(这很好),或者它可以是smorsh,trale,gullup或者闪烁(这些都是不可接受的)。 如果它是smorsh它必须是unmorshed; 如果它是trale它必须是baludoed; 如果它是gullup它必须小跑(然后也可能节奏); 如果它闪烁,它必须再次闪烁,除了星期四。 你明白这个想法:这很复杂。 但最终结果是(或应该)所有条目都是干净的。 Right Thing(TM)要做的是总结清洁循环的效果

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

    对于每个试图理解精彩循环实现的确切内容的人来说,这个陈述让人头疼。 而这些人中最常见的可能就是你自己。

  4. 告知计算机您的代码在某些时候已经实现了什么。
    如果您在小跑之后忘记调整需要它的条目,那么assert将节省您的一天,并避免您的代码在Daphne之后发生。

在我看来, assert文件(1和3)和保障(2和4)的两个目的同样有价值。
告知人们甚至可能比告知计算机有价值,因为它可以防止assert旨在捕获的错误(在案例1中)以及在任何情况下的大量后续错误。


#5楼

是否存在性能问题?

  • 请记住“在你快速工作之前先让它工作”
    任何程序的百分之几通常与其速度相关。 如果它被证明是一个性能问题,你总是可以踢出或简化assert - 而且大多数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!

    这很便宜,但很可能会捕获大部分实际程序错误。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!