Best practice for Python assert
使用
是
1 | assert x >= 0, 'x is less than zero' |
好或坏于
1 2 | if x < 0: raise Exception, 'x is less than zero' |
此外,是否有任何方法可以设置一个像
断言应该用于测试不应该发生的条件。目的是在程序状态损坏的情况下尽早崩溃。
异常应该用于可能发生的错误,并且您应该几乎总是创建自己的异常类。
例如,如果要将函数从配置文件读取到
在您的示例中,如果
如果
"assert"语句在编译优化时被删除。所以,是的,性能和功能都有区别。
The current code generator emits no code for an assert statement when optimization is requested at compile time. - Python 2.6.4 Docs
如果您使用
参见pythonoptimeize和-o-oo
在整个函数中,当x小于零时,能够自动抛出错误。可以使用类描述符。下面是一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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"<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 |
假设您与四位同事Alice、Bernd、Carl和Daphne一起处理200000行代码。他们叫你的代码,你叫他们的代码。
那么,
通知Alice、Bernd、Carl和Daphne您的代码需要什么。假设您有一个处理元组列表的方法,并且如果这些元组不可变,则程序逻辑可能中断:
1 2 | def mymethod(listOfTuples): assert(all(type(tp)==tuple for tp in listOfTuples)) |
这比文档中的等效信息更可靠更容易维护。
通知计算机您的代码需要什么。
告诉读者您的代码(包括您自己)您的代码在某个时刻已经实现了什么。假设您有一个条目列表,并且每个条目都可以是干净的(这很好)也可以是smorsh、trale、gullup或flinkle(这些都是不可接受的)。如果它是烟雾,它一定是不郁闷的;如果它是垃圾,它一定是栏杆做的;如果它是海鸥,它必须小跑(然后可能步调,也);如果它闪烁,必须再次闪烁,除了星期四。你明白了:这是复杂的事情。但最终的结果是(或应该是)所有条目都是干净的。正确的做法是总结清洁回路组件
1 | assert(all(entry.isClean() for entry in mylist)) |
这句话让每个试图理解的人都头疼。正是这个奇妙的循环正在实现。这些人中最频繁的可能就是你自己。
告诉计算机你的代码在某个时刻已经实现了什么。如果你在小跑后忘了用步调调整一个需要的项目,
在我看来,
除了其他答案外,断言本身抛出异常,但只抛出断言错误。从功利主义的角度来看,断言不适用于需要精细控制捕获哪些异常的情况。
这种方法唯一真正错误的地方是,使用断言语句很难产生非常具描述性的异常。如果您在寻找更简单的语法,请记住您也可以这样做:
1 2 3 4 5 6 7 8 9 10 | class XLessThanZeroException(Exception): pass def CheckX(x): if x < 0: raise XLessThanZeroException() def foo(x): CheckX(x) #do stuff here |
另一个问题是,使用assert进行正常条件检查会使使用-o标志禁用调试断言变得困难。
如前所述,当您的代码不应该达到某一点时,应该使用断言,这意味着那里有一个bug。我能看到使用断言的最有用的原因可能是不变量/前置/后置条件。在循环或函数的每次迭代的开始或结束时,这些都必须是真的。
例如,递归函数(2个独立的函数,1个处理错误的输入,另一个处理错误的代码,因为很难区分递归)。如果我忘了写if语句,什么地方出错了,这就很明显了。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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是指宣誓、肯定、声明。这并不意味着"检查"或"应该检查"。这意味着你作为一个编码员在这里做了一个宣誓声明:
1 2 3 | # 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 被证明是成为一个性能问题——而且他们中的大多数永远不会。务实:假设您有一个方法来处理一个非空的元组列表,并且如果这些元组不是不可变的,那么程序逻辑将中断。你应该写:
1
2def mymethod(listOfTuples):
assert(all(type(tp)==tuple for tp in listOfTuples))如果您的列表往往有十个条目长,这可能很好,但是如果他们有一百万个条目,这可能会成为一个问题。但你可以不把这张有价值的支票全部扔掉只需将其降级为
1
2def mymethod(listOfTuples):
assert(type(listOfTuples[0])==tuple) # in fact _all_ must be tuples!这是便宜的,但很可能会捕捉到大多数实际的程序错误。
断言是要检查的-
1。有效条件,
2。有效声明,
三。真逻辑;
源代码的。它并没有使整个项目失败,而是发出一个警报,提示您的源文件中存在不适当的内容。
在示例1中,因为变量"str"不是nul。因此不会引发任何断言或异常。
例1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #!/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。所以我们通过断言语句来避免用户提前执行错误的程序。
例2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #!/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 |
当我们不希望调试并在源代码中实现断言问题时。禁用优化标志
python-o断言语句.py
什么也得不到印刷品
Java有一个JBaseDROLLS框架,它运行运行时监控来维护业务规则,它回答了问题的第二部分。但是,我不确定是否有这样一个针对Python的框架。
在诸如ptvs、pycharm、wing-
如果您处理的是依赖
1 2 3 4 5 | try: assert False raise Exception('Python Assertions are not working. This tool relies on Python Assertions to do its job. Possible causes are running with the"-O" flag or running a precompiled (".pyo" or".pyc") module.') except AssertionError: pass |