使用断言的最佳实践?
使用
assert
作为标准代码的一部分而不是仅用于调试目的是否会出现性能或代码维护问题?是
断言 x >= 0, 'x 小于零'
比更好或更差
如果 x < 0: 引发异常('x 小于零')
- 另外,有没有办法设置像
if x << 0 引发错误
,始终在没有try/ except/finally
的情况下进行检查,因此,如果在整个代码中的任何时候x
小于 0,则会引发错误,就像你设置assert x < 0
在函数开始处,函数内任何x
小于 0 的地方都会引发异常?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(16)
英语单词断言在这里的意思是发誓、确认< /em>,承认。 它并不意味着“检查”或“应该”。 这意味着您作为编码员在此做出宣誓声明:
如果代码正确,则禁止单事件干扰、硬件故障等,任何断言都不会失败。 这就是为什么程序对最终用户的行为不能受到影响。 特别是,即使在特殊的编程条件下,断言也不会失败。 它只是永远不会发生。 如果发生这种情况,程序员就应该受到惩罚。
The English language word assert here is used in the sense of swear, affirm, avow. It doesn't mean "check" or "should be". It means that you as a coder are making a sworn statement here:
If the code is correct, barring Single-event upsets, hardware failures and such, no assert will ever fail. That is why the behaviour of the program to an end user must not be affected. Especially, an assert cannot fail even under exceptional programmatic conditions. It just doesn't ever happen. If it happens, the programmer should be zapped for it.
正如前面所说,当您的代码不应该达到某个点(这意味着那里存在错误)时,应该使用断言。 我认为使用断言的最有用的原因可能是不变/前置/后置条件。 这些在循环或函数的每次迭代开始或结束时必须为真。
例如,递归函数(2 个独立的函数,因此 1 个处理错误的输入,另一个处理错误的代码,因为很难与递归区分开)。 如果我忘记写 if 语句,这会很明显地表明出了什么问题。
这些循环不变量通常可以用断言来表示。
As has been said previously, assertions should be used when your code SHOULD NOT ever reach a point, meaning there is a bug there. Probably the most useful reason I can see to use an assertion is an invariant/pre/postcondition. These are something that must be true at the start or end of each iteration of a loop or a function.
For example, a recursive function (2 seperate functions so 1 handles bad input and the other handles bad code, cause it's hard to distinguish with recursion). This would make it obvious if I forgot to write the if statement, what had gone wrong.
These loop invariants often can be represented with an assertion.
无论如何,如果您正在处理依赖
assert
才能正常运行的代码,那么添加以下代码将确保启用断言:For what it's worth, if you're dealing with code which relies on
assert
to function properly, then adding the following code will ensure that asserts are enabled:嗯,这是一个悬而未决的问题,我想谈两个方面:何时添加断言以及如何编写错误消息。
目的
向初学者解释一下 - 断言是可能引发错误的语句,但您不会捕获它们。 它们通常不应该被抚养,但在现实生活中,有时它们确实会被抚养。 这是一种严重的情况,代码无法从中恢复,我们称之为“致命错误”。
接下来,它是为了“调试目的”,虽然这是正确的,但听起来非常不屑一顾。 我更喜欢“声明不变量,永远不应该违反”的表述,尽管它对不同的初学者有不同的作用......有些人“只是明白”,而另一些人要么没有发现它有任何用处,要么替换正常的异常,甚至用它来控制流量。
风格
在 Python 中,
assert
是一条语句,而不是一个函数! (记住assert(False, 'is true')
不会引发。但是,解决这个问题:何时以及如何编写可选的“错误消息”?
通常有许多专用方法来执行断言(
assertTrue(condition)
、assertFalse(condition)、assertEqual(actual, Expected)
等)。在某些情况下,您可以在不需要的代码中
对断言进行注释:
def dump(something):
断言 isinstance(某物,可转储)
# ...
但除此之外,消息对于与其他程序员(有时是代码的交互式用户,例如在 Ipython/Jupyter 等中)进行通信很有用。
向他们提供信息,而不仅仅是泄露内部实施细节。
而不是:
写:
或者甚至:
我知道,我知道 - 这不是静态断言的情况,但我想指出消息的信息价值。
负面信息还是正面信息?
这可能是有争议的,但读到这样的东西让我很伤心:
这是两个相互矛盾的东西写在一起。 因此,每当我对代码库产生影响时,我都会通过使用“必须”和“应该”等额外动词来推动指定我们想要什么,而不是说我们不想要什么。
断言a == b,'a必须等于b'
然后,得到
AssertionError: a must be equal to b
也是可读的,并且该语句在代码中看起来很合乎逻辑。 此外,您可以在不读取回溯的情况下从中获取一些东西(有时甚至不可用)。Well, this is an open question, and I have two aspects that I want to touch on: when to add assertions and how to write the error messages.
Purpose
To explain it to a beginner - assertions are statements which can raise errors, but you won't be catching them. And they normally should not be raised, but in real life they sometimes do get raised anyway. And this is a serious situation, which the code cannot recover from, what we call a 'fatal error'.
Next, it's for 'debugging purposes', which, while correct, sounds very dismissive. I like the 'declaring invariants, which should never be violated' formulation better, although it works differently on different beginners... Some 'just get it', and others either don't find any use for it, or replace normal exceptions, or even control flow with it.
Style
In Python,
assert
is a statement, not a function! (rememberassert(False, 'is true')
will not raise. But, having that out of the way:When, and how, to write the optional 'error message'?
This acually applies to unit testing frameworks, which often have many dedicated methods to do assertions (
assertTrue(condition)
,assertFalse(condition), assertEqual(actual, expected)
etc.). They often also provide a way to comment on the assertion.In throw-away code you could do without the error messages.
In some cases, there is nothing to add to the assertion:
def dump(something):
assert isinstance(something, Dumpable)
# ...
But apart from that, a message is useful for communication with other programmers (which are sometimes interactive users of your code, e.g. in Ipython/Jupyter etc.).
Give them information, not just leak internal implementation details.
instead of:
write:
or maybe even:
I know, I know - this is not a case for a static assertion, but I want to point to the informational value of the message.
Negative or positive message?
This may be conroversial, but it hurts me to read things like:
these are two contradictory things written next to eachother. So whenever I have an influence on the codebase, I push for specifying what we want, by using extra verbs like 'must' and 'should', and not to say what we don't want.
assert a == b, 'a must be equal to b'
Then, getting
AssertionError: a must be equal to b
is also readable, and the statement looks logical in code. Also, you can get something out of it without reading the traceback (which can sometimes not even be available).是否存在性能问题?
请记住“先让它工作,然后再让它快速工作”。
任何程序中很少有一部分与其速度相关。
如果事实证明,您始终可以删除或简化
断言
成为一个性能问题——而且大多数永远不会。
务实:
假设您有一个处理非空元组列表的方法,并且如果这些元组不是不可变的,则程序逻辑将中断。 你应该写:
如果您的列表往往有十个条目那么这可能没问题,但是
如果他们有一百万个条目,这可能会成为一个问题。
但与其完全丢弃这张有价值的支票,您可以
只需将其降级为
这很便宜,但无论如何都可能捕获大多数实际程序错误。
Is there a performance issue?
Please remember to "make it work first before you make it work fast".
Very few percent of any program are usually relevant for its speed.
You can always kick out or simplify an
assert
if it ever proves tobe a performance problem -- and most of them never will.
Be pragmatic:
Assume you have a method that processes a non-empty list of tuples and the program logic will break if those tuples are not immutable. You should write:
This is probably fine if your lists tend to be ten entries long, but
it can become a problem if they have a million entries.
But rather than discarding this valuable check entirely you could
simply downgrade it to
which is cheap but will likely catch most of the actual program errors anyway.
assert
的使用和异常的引发都与通信有关。断言是针对开发人员的关于代码正确性的声明:代码中的断言告知代码的读者有关代码正确性必须满足的条件。 运行时失败的断言会通知开发人员代码中存在需要修复的缺陷。
异常是关于非典型情况的指示,这些情况可能在运行时发生,但无法由手头的代码解决,在要在那里处理的调用代码处解决。 异常的发生并不表明代码中存在错误。
因此,如果您将运行时出现的特定情况视为错误,您希望通知开发人员(“开发人员您好,此情况表明某处存在错误,请修复代码。” )然后进行断言。 如果断言检查代码的输入参数,则通常应在文档中添加当输入参数违反该条件时代码具有“未定义的行为”的信息。
相反,如果这种情况的发生并不表明您认为存在错误,而是您认为应该由客户端代码处理的(可能罕见但)可能的情况,请引发异常。 引发异常的情况应该是相应代码文档的一部分。
断言的评估需要一些时间。 不过,它们可以在编译时消除。 然而,这会产生一些后果,请参见下文。
通常断言可以提高代码的可维护性,因为它们通过明确假设并在运行时定期验证这些假设来提高可读性。 这也将有助于捕捉回归。 然而,需要牢记一个问题:断言中使用的表达式不应有副作用。 如上所述,可以在编译时消除断言 - 这意味着潜在的副作用也会消失。 这可能会无意中改变代码的行为。
Both the use of
assert
and the raising of exceptions are about communication.Assertions are statements about the correctness of code addressed at developers: An assertion in the code informs readers of the code about conditions that have to be fulfilled for the code being correct. An assertion that fails at run-time informs developers that there is a defect in the code that needs fixing.
Exceptions are indications about non-typical situations that can occur at run-time but can not be resolved by the code at hand, addressed at the calling code to be handled there. The occurence of an exception does not indicate that there is a bug in the code.
Therefore, if you consider the occurence of a specific situation at run-time as a bug that you would like to inform the developers about ("Hi developer, this condition indicates that there is a bug somewhere, please fix the code.") then go for an assertion. If the assertion checks input arguments of your code, you should typically add to the documentation that your code has "undefined behaviour" when the input arguments violate that conditions.
If instead the occurrence of that very situation is not an indication of a bug in your eyes, but instead a (maybe rare but) possible situation that you think should rather be handled by the client code, raise an exception. The situations when which exception is raised should be part of the documentation of the respective code.
The evaluation of assertions takes some time. They can be eliminated at compile time, though. This has some consequences, however, see below.
Normally assertions improve the maintainability of the code, since they improve readability by making assumptions explicit and during run-time regularly verifying these assumptions. This will also help catching regressions. There is one issue, however, that needs to be kept in mind: Expressions used in assertions should have no side-effects. As mentioned above, assertions can be eliminated at compile time - which means that also the potential side-effects would disappear. This can - unintendedly - change the behaviour of the code.
断言是要检查 -
1. 有效条件,
2. 有效声明,
3.真正的逻辑;
的源代码。 它不会使整个项目失败,而是发出警报,表明源文件中存在某些不合适的内容。
在示例 1 中,由于变量“str”不为空。 因此不会引发任何断言或异常。
示例 1:
在示例 2 中,var 'str' 为 null。 因此,我们通过 assert 语句来避免用户继续执行错误的程序。
示例2:
当我们不想调试并意识到源代码中的断言问题时。 禁用优化标志
python -O assertStatement.py
什么都不会打印
An Assert is to check -
1. the valid condition,
2. the valid statement,
3. true logic;
of source code. Instead of failing the whole project it gives an alarm that something is not appropriate in your source file.
In example 1, since variable 'str' is not null. So no any assert or exception get raised.
Example 1:
In example 2, var 'str' is null. So we are saving the user from going ahead of faulty program by assert statement.
Example 2:
The moment we don't want debug and realized the assertion issue in the source code. Disable the optimization flag
python -O assertStatement.py
nothing will get print
在 PTVS、PyCharm、Wing 等 IDE 中,可以使用
assert isinstance()
语句来为一些不清楚的对象启用代码补全。In IDE's such as PTVS, PyCharm, Wing
assert isinstance()
statements can be used to enable code completion for some unclear objects.我要补充的是,我经常使用
assert
来指定属性,例如 循环不变量 或我的代码应该具有的逻辑属性,就像我在正式验证的软件中指定它们一样。它们的目的既是告知读者,帮助我推理,并检查我的推理没有犯错误。 例如:
或者在更复杂的情况下:
由于当 python 在优化模式下运行时断言被禁用,因此请毫不犹豫地在其中编写昂贵的条件,特别是如果它使您的代码更清晰和更少容易出现错误
I'd add I often use
assert
to specify properties such as loop invariants or logical properties my code should have, much like I'd specify them in formally-verified software.They serve both the purpose of informing readers, helping me reason, and checking I am not making a mistake in my reasoning. For example :
or in more complicated situations:
Since asserts are disabled when python is run in optimized mode, do not hesitate to write costly conditions in them, especially if it makes your code clearer and less bug-prone
设置业务规则,例如
if x < 0 引发错误
据我所知,对此没有通用的(与实现无关的)解决方案。
但是,如果
x
是某个类C
的对象的属性,则可以通过将普通属性x
转换为x
来完成strong>属性x
不仅有 getter,还有 setter:https://docs.python.org/3/library/functions.html#property
然后,setter 可以实现并强制执行业务规则。
Set a business rule like
if x < 0 raise error
As far as I am aware, there is no general (implementation-independent) solution for this.
If, however,
x
is an attribute of the objects of some classC
, it could be done by turning the plain attributex
into a propertyx
that has not only a getter but also a setter:https://docs.python.org/3/library/functions.html#property
The setter could then implement and enforce the business rule.
断言应用于测试不应该发生的条件。 目的是在程序状态损坏的情况下尽早崩溃。
异常应该用于可能发生的错误,并且您几乎应该总是创建自己的异常类。
例如,如果您正在编写一个从配置文件读取到
dict
的函数,则文件中的格式不正确会引发ConfigurationSyntaxError
,而您可以断言
您不会返回None
。在您的示例中,如果 x 是通过用户界面或外部源设置的值,则最好是例外。
如果 x 仅由您自己的代码在同一程序中设置,请使用断言。
Asserts should be used to test conditions that should never happen. The purpose is to crash early in the case of a corrupt program state.
Exceptions should be used for errors that can conceivably happen, and you should almost always create your own Exception classes.
For example, if you're writing a function to read from a configuration file into a
dict
, improper formatting in the file should raise aConfigurationSyntaxError
, while you canassert
that you're not about to returnNone
.In your example, if
x
is a value set via a user interface or from an external source, an exception is best.If
x
is only set by your own code in the same program, go with an assertion.优化编译时删除“assert”语句。 所以,是的,性能和功能都存在差异。
如果您使用
assert
来实现应用程序功能,那么优化生产部署,您将受到“but-it-works-in-dev”缺陷的困扰。请参阅 PYTHONOPTIMIZE 和 -O -OO
"assert" statements are removed when the compilation is optimized. So, yes, there are both performance and functional differences.
If you use
assert
to implement application functionality, then optimize the deployment to production, you will be plagued by "but-it-works-in-dev" defects.See PYTHONOPTIMIZE and -O -OO
当整个函数中 x 小于零时能够自动抛出错误。 您可以使用类描述符。 这是一个例子:
To be able to automatically throw an error when x become less than zero throughout the function. You can use class descriptors. Here is an example:
assert
的四个目的假设您与四位同事 Alice、Bernd、Carl 和 Daphne 一起处理 200,000 行代码。
他们调用您的代码,您调用他们的代码。
那么
assert
有四个角色:告知 Alice、Bernd、Carl 和 Daphne 您的代码期望什么。
假设您有一个处理元组列表的方法,并且如果这些元组不是不可变的,则程序逻辑可能会中断:
这比文档中的同等信息更值得信赖
并且更容易维护。
告知计算机您的代码需要什么。
assert
强制代码调用者采取正确的行为。如果你的代码调用 Alices 的代码,而 Bernd 的代码调用你的代码,
然后,如果没有
assert
,如果程序在 Alice 代码中崩溃,伯恩德可能认为这是爱丽丝的错,
爱丽丝调查后可能会认为这是你的错,
你调查并告诉伯恩德这实际上是他的。
很多工作都丢掉了。
有了断言,无论谁接到错误的电话,他们很快就能发现这是错误的
他们的错,不是你的。 爱丽丝、伯恩德和你们都会受益。
节省大量时间。
告知您的代码的读者(包括您自己)您的代码在某个时刻取得了哪些成就。
假设您有一个条目列表,并且每个条目都可以是干净的(这很好)
或者它可以是smorsh、trale、gullup或twinkled(这些都是不可接受的)。
如果它是smosh的,那么它必须是unsmoshed的; 如果它是真实的,那么它一定是歪曲的;
如果它是咕噜咕噜的,那么它必须小跑(然后可能也有节奏);
如果它闪烁,除了星期四之外,它必须再次闪烁。
你明白了:这是很复杂的事情。
但最终结果是(或应该是)所有条目都是干净的。
正确的事情(TM) 要做的是总结您的影响
清洁循环为
这种说法让每个试图理解的人都感到头疼
奇妙的循环正在实现什么到底。
这些人中最常出现的可能就是您自己。
告知计算机您的代码在某个时刻取得了哪些成果。
如果您在小跑后忘记为需要它的条目配速,
assert
将节省您的时间并避免您的代码亲爱的达芙妮的打破要晚得多。
在我看来,
assert
文档的两个目的(1 和 3)和保障措施(2 和 4)同样有价值。
通知人们甚至可能比通知计算机更有价值
因为它可以防止
assert
旨在捕获的错误(在情况 1 中)无论如何,随后还会犯很多错误。
The four purposes of
assert
Assume you work on 200,000 lines of code with four colleagues Alice, Bernd, Carl, and Daphne.
They call your code, you call their code.
Then
assert
has four roles:Inform Alice, Bernd, Carl, and Daphne what your code expects.
Assume you have a method that processes a list of tuples and the program logic can break if those tuples are not immutable:
This is more trustworthy than equivalent information in the documentation
and much easier to maintain.
Inform the computer what your code expects.
assert
enforces proper behavior from the callers of your code.If your code calls Alices's and Bernd's code calls yours,
then without the
assert
, if the program crashes in Alices code,Bernd might assume it was Alice's fault,
Alice investigates and might assume it was your fault,
you investigate and tell Bernd it was in fact his.
Lots of work lost.
With asserts, whoever gets a call wrong, they will quickly be able to see it was
their fault, not yours. Alice, Bernd, and you all benefit.
Saves immense amounts of time.
Inform the readers of your code (including yourself) what your code has achieved at some point.
Assume you have a list of entries and each of them can be clean (which is good)
or it can be smorsh, trale, gullup, or twinkled (which are all not acceptable).
If it's smorsh it must be unsmorshed; if it's trale it must be baludoed;
if it's gullup it must be trotted (and then possibly paced, too);
if it's twinkled it must be twinkled again except on Thursdays.
You get the idea: It's complicated stuff.
But the end result is (or ought to be) that all entries are clean.
The Right Thing(TM) to do is to summarize the effect of your
cleaning loop as
This statements saves a headache for everybody trying to understand
what exactly it is that the wonderful loop is achieving.
And the most frequent of these people will likely be yourself.
Inform the computer what your code has achieved at some point.
Should you ever forget to pace an entry needing it after trotting,
the
assert
will save your day and avoid that your codebreaks dear Daphne's much later.
In my mind,
assert
's two purposes of documentation (1 and 3) andsafeguard (2 and 4) are equally valuable.
Informing the people may even be more valuable than informing the computer
because it can prevent the very mistakes the
assert
aims to catch (in case 1)and plenty of subsequent mistakes in any case.
除了其他答案之外,断言本身也会引发异常,但仅限于 AssertionErrors。 从功利主义的角度来看,当您需要对捕获的异常进行细粒度控制时,断言不适合。
In addition to the other answers, asserts themselves throw exceptions, but only AssertionErrors. From a utilitarian standpoint, assertions aren't suitable for when you need fine grain control over which exceptions you catch.
这种方法唯一真正错误的是,很难使用断言语句做出非常具有描述性的异常。 如果您正在寻找更简单的语法,请记住您可以也可以执行以下操作:
另一个问题是使用断言进行正常条件检查,这使得使用断言禁用调试断言变得困难-O 标志。
The only thing that's really wrong with this approach is that it's hard to make a very descriptive exception using assert statements. If you're looking for the simpler syntax, remember you can also do something like this:
Another problem is that using assert for normal condition-checking is that it makes it difficult to disable the debugging asserts using the -O flag.