Is there a performance or code maintenance issue with using assert as part of the standard code instead of using it just for debugging purposes? Is assert x >= 0, 'x is less than zero' better or worse than if x < 0: raise Exception('x is less than zero') Also, is there any way to set a business rule like if x < 0 raise error that is always checked without the try/except/finally so, if at anytime throughout the code x is less than 0 an error is raised, like if you set assert x < 0 at the start of a function, anywhere within the function where x becomes less then 0 an exception is raised?
当前回答
当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 "<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
其他回答
在诸如PTVS、PyCharm、Wing assert isinstance()等IDE中,可以使用isinstance语句对一些不清楚的对象启用代码补全。
这是一个悬而未决的问题,我有两个方面想涉及:何时添加断言以及如何编写错误消息。
目的
向初学者解释一下,断言是一种可能引发错误的语句,但您不会捕获它们。他们通常不应该被抚养,但在现实生活中,他们有时还是会被抚养。这是一个严重的情况,代码无法恢复,我们称之为“致命错误”。
其次,它是为了“调试目的”,虽然正确,但听起来非常轻蔑。我更喜欢“声明不变量,它永远不应该被违反”的提法,尽管它对不同的初学者有不同的作用……有些人“只是得到它”,而另一些人要么没有找到它的任何用途,要么取代正常的异常,甚至用它来控制流。
风格
在Python中,assert是语句,而不是函数!(记住assert(False, 'is true')不会引发。但是,先说一下:
何时以及如何编写可选的“错误消息”?
这实际上适用于单元测试框架,它通常有许多专用的方法来执行断言(assertTrue(条件),assertFalse(条件),assertEqual(实际的,预期的)等)。它们通常还提供了一种对断言进行评论的方法。
在一次性代码中,您可以不使用错误消息。
在某些情况下,没有什么可以添加到断言:
def垃圾场(东西): 屁 # ...
但除此之外,消息对于与其他程序员(有时是你代码的交互式用户,例如在Ipython/Jupyter等)的交流是有用的。
给他们信息,而不仅仅是泄露内部实现细节。
而不是:
assert meaningless_identifier <= MAGIC_NUMBER_XXX, 'meaningless_identifier is greater than MAGIC_NUMBER_XXX!!!'
写:
assert meaningless_identifier > MAGIC_NUMBER_XXX, 'reactor temperature above critical threshold'
或者甚至:
assert meaningless_identifier > MAGIC_NUMBER_XXX, f'reactor temperature({meaningless_identifier }) above critical threshold ({MAGIC_NUMBER_XXX})'
我知道,我知道——这不是静态断言的情况,但我想指出消息的信息值。
消极还是积极的信息?
这可能是有争议的,但读到这样的东西让我很受伤:
assert a == b, 'a is not equal to b'
这是两个相互矛盾的东西。因此,每当我对代码库产生影响时,我就会通过使用“必须”和“应该”等额外的动词来明确我们想要什么,而不是说我们不想要什么。 断言a == b, 'a必须等于b'
然后,获取AssertionError: a must equal to b也是可读的,并且语句在代码中看起来是合乎逻辑的。此外,您可以在不读取回溯(有时甚至不可用)的情况下从中获得一些信息。
当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 "<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
我补充说,我经常使用断言来指定属性,比如循环不变量或我的代码应该具有的逻辑属性,就像我在正式验证的软件中指定它们一样。
它们有两个目的,告诉读者,帮助我推理,并检查我在推理中没有犯错误。例如 :
k = 0
for i in range(n):
assert k == i * (i + 1) // 2
k += i
#do some things
或者在更复杂的情况下:
def sorted(l):
return all(l1 <= l2 for l1, l2 in zip(l, l[1:]))
def mergesort(l):
if len(l) < 2: #python 3.10 will have match - case for this instead of checking length
return l
k = len(l // 2)
l1 = mergesort(l[:k])
l2 = mergesort(l[k:])
assert sorted(l1) # here the asserts allow me to explicit what properties my code should have
assert sorted(l2) # I expect them to be disabled in a production build
return merge(l1, l2)
因为当python在优化模式下运行时,断言是禁用的,所以不要犹豫在它们中编写代价高昂的条件,特别是当它使您的代码更清晰,更不容易出现错误时
这种方法唯一真正的错误是,很难使用断言语句产生非常描述性的异常。如果你正在寻找更简单的语法,记住你也可以这样做:
class XLessThanZeroException(Exception):
pass
def CheckX(x):
if x < 0:
raise XLessThanZeroException()
def foo(x):
CheckX(x)
#do stuff here
另一个问题是,使用assert进行正常的条件检查会使使用-O标志禁用调试断言变得困难。
推荐文章
- Numpy Max vs amax vs maximum
- 我应该在.gitignore文件中添加Django迁移文件吗?
- 每n行有熊猫
- 实例属性attribute_name定义在__init__之外
- 如何获取在Python中捕获的异常的名称?
- 第一次出现的值大于现有值的Numpy
- 如何从Python函数中返回两个值?
- 前一个月的Python日期
- Python中方括号括起来的列表和圆括号括起来的列表有什么区别?
- Python日志记录不输出任何东西
- 每n秒运行特定代码
- SQLAlchemy是否有与Django的get_or_create等价的函数?
- 如何将python datetime转换为字符串,具有可读格式的日期?
- 美丽的汤和提取div及其内容的ID
- 断言是邪恶的吗?