标签归档:raise

如何在嵌套的try / except块中重新引发异常?

问题:如何在嵌套的try / except块中重新引发异常?

我知道如果我想重新引发异常,我会raise在相应的except块中简单地使用不带参数的形式。但是给定一个嵌套的表达式

try:
    something()
except SomeError as e:
    try:
        plan_B()
    except AlsoFailsError:
        raise e  # I'd like to raise the SomeError as if plan_B()
                 # didn't raise the AlsoFailsError

我如何在SomeError不破坏堆栈跟踪的情况下重新筹集?raise在这种情况下,仅此一项便会重新提高AlsoFailsError。或者我该如何重构我的代码来避免此问题?

I know that if I want to re-raise an exception, I simple use raise without arguments in the respective except block. But given a nested expression like

try:
    something()
except SomeError as e:
    try:
        plan_B()
    except AlsoFailsError:
        raise e  # I'd like to raise the SomeError as if plan_B()
                 # didn't raise the AlsoFailsError

how can I re-raise the SomeError without breaking the stack trace? raise alone would in this case re-raise the more recent AlsoFailsError. Or how could I refactor my code to avoid this issue?


回答 0

从Python 3开始,回溯存储在异常中,因此raise e(大多数)正确的事情很简单:

try:
    something()
except SomeError as e:
    try:
        plan_B()
    except AlsoFailsError:
        raise e  # or raise e from None - see below

产生的追溯将包括SomeError在处理过程中发生的其他通知AlsoFailsError(由于位于raise e内部except AlsoFailsError)。这具有误导性,因为实际发生的是相反的情况-我们AlsoFailsError在尝试从恢复时遇到并处理了它SomeError。要获取不包含的回溯AlsoFailsError,请替换raise eraise e from None

在Python 2中,您将异常类型,值和回溯存储在局部变量中,并使用以下三个参数的形式raise

try:
    something()
except SomeError:
    t, v, tb = sys.exc_info()
    try:
        plan_B()
    except AlsoFailsError:
        raise t, v, tb

As of Python 3 the traceback is stored in the exception, so a simple raise e will do the (mostly) right thing:

try:
    something()
except SomeError as e:
    try:
        plan_B()
    except AlsoFailsError:
        raise e  # or raise e from None - see below

The traceback produced will include an additional notice that SomeError occurred while handling AlsoFailsError (because of raise e being inside except AlsoFailsError). This is misleading because what actually happened is the other way around – we encountered AlsoFailsError, and handled it, while trying to recover from SomeError. To obtain a traceback that doesn’t include AlsoFailsError, replace raise e with raise e from None.

In Python 2 you’d store the exception type, value, and traceback in local variables and use the three-argument form of raise:

try:
    something()
except SomeError:
    t, v, tb = sys.exc_info()
    try:
        plan_B()
    except AlsoFailsError:
        raise t, v, tb

回答 1

即使接受的解决方案正确,也最好使用指向具有Python 2 + 3解决方案的Sixsix.reraise

六。重新提高exc_typeexc_valueexc_traceback = None)

重新引发异常,可能使用不同的回溯。[…]

因此,您可以编写:

import six


try:
    something()
except SomeError:
    t, v, tb = sys.exc_info()
    try:
        plan_B()
    except AlsoFailsError:
        six.reraise(t, v, tb)

Even if the accepted solution is right, it’s good to point to the Six library which has a Python 2+3 solution, using six.reraise.

six.reraise(exc_type, exc_value, exc_traceback=None)

Reraise an exception, possibly with a different traceback. […]

So, you can write:

import six


try:
    something()
except SomeError:
    t, v, tb = sys.exc_info()
    try:
        plan_B()
    except AlsoFailsError:
        six.reraise(t, v, tb)

回答 2

根据Drew McGowen的建议,但考虑到一般情况(存在返回值s),这是user4815162342的替代方法:

try:
    s = something()
except SomeError as e:
    def wrapped_plan_B():
        try:
            return False, plan_B()
        except:
            return True, None
    failed, s = wrapped_plan_B()
    if failed:
        raise

As per Drew McGowen’s suggestion, but taking care of a general case (where a return value s is present), here’s an alternative to user4815162342’s answer:

try:
    s = something()
except SomeError as e:
    def wrapped_plan_B():
        try:
            return False, plan_B()
        except:
            return True, None
    failed, s = wrapped_plan_B()
    if failed:
        raise

回答 3

Python 3.5+始终将追溯信息附加到错误,因此不再需要单独保存它。

>>> def f():
...   try:
...     raise SyntaxError
...   except Exception as e:
...     err = e
...     try:
...       raise AttributeError
...     except Exception as e1:
...       raise err from None
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 9, in f
  File "<stdin>", line 3, in f
SyntaxError: None
>>> 

Python 3.5+ attaches the traceback information to the error anyway, so it’s no longer necessary to save it separately.

>>> def f():
...   try:
...     raise SyntaxError
...   except Exception as e:
...     err = e
...     try:
...       raise AttributeError
...     except Exception as e1:
...       raise err from None
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 9, in f
  File "<stdin>", line 3, in f
SyntaxError: None
>>> 

Python断言的最佳实践

问题:Python断言的最佳实践

  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. 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'
    
  2. 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?


回答 0

为了能够在整个函数中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

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:

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

回答 1

应该使用断言来测试永远不会发生的条件。目的是在程序状态损坏的情况下尽早崩溃。

应该将异常用于可能发生的错误,并且几乎应该始终创建自己的Exception类


例如,如果您要编写一个从配置文件读取到的函数,则文件中dict不正确的格式将引发a ConfigurationSyntaxError,同时assert您可以避免返回None


在您的示例中,如果x是通过用户界面或外部来源设置的值,则最好是exceptions。

如果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 a ConfigurationSyntaxError, while you can assert that you’re not about to return None.


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.


回答 2

优化编译后,将删除“ assert”语句。因此,是的,在性能和功能上都存在差异。

当在编译时请求优化时,当前代码生成器不会为assert语句生成任何代码。- Python 2的文档 的Python 3文档

如果您用于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.

The current code generator emits no code for an assert statement when optimization is requested at compile time. – Python 2 Docs Python 3 Docs

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


回答 3

的四个目的 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. 通知您的代码(包括您自己)的读者在某些时候取得了什么成就。
    假设您有一个条目列表,并且每个条目都可以是干净的(很好),也可以是乱码,流浪汉,gullup或闪烁的(都不可接受)。如果它很轻便,必须将其清零。如果是流浪汉,则必须加以保护;如果它是古怪的,则必须将其放小(然后也可能要加快速度);如果已闪烁,则必须再次闪烁(星期四除外)。您会明白:这是复杂的东西。但是最终结果是(或应该是)所有条目都是干净的。Right Thing(TM)要做的是将清洁循环的效果总结为

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

    该报表节省了大家试图了解头痛究竟它是一个美妙的循环实现。这些人中最常出现的人可能就是你自己。

  4. 通知计算机您的代码在某些时候已经实现了什么。
    如果您在小跑后忘记了需要它的条目,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:

  1. 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:

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

    This is more trustworthy than equivalent information in the documentation and much easier to maintain.

  2. 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.

  3. 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

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

    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.

  4. 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 code breaks dear Daphne’s much later.

In my mind, assert‘s two purposes of documentation (1 and 3) and safeguard (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.


回答 4

除了其他答案外,断言本身会引发异常,但仅断言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.


回答 5

这种方法唯一真正出错的地方是,使用assert语句很难创建非常描述性的异常。如果您正在寻找更简单的语法,请记住您可以执行以下操作:

class XLessThanZeroException(Exception):
    pass

def CheckX(x):
    if x < 0:
        raise XLessThanZeroException()

def foo(x):
    CheckX(x)
    #do stuff here

另一个问题是,使用assert进行正常的条件检查是,使用-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:

class XLessThanZeroException(Exception):
    pass

def CheckX(x):
    if x < 0:
        raise XLessThanZeroException()

def foo(x):
    CheckX(x)
    #do stuff here

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.


回答 6

英语语言文字断言在这里的意义上使用发誓申明招认。这并不意味着“检查”“应该”。这意味着作为编码人员正在此处宣誓就职

# 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

如果代码正确,则除非发生单事件失败,硬件故障等,否则断言不会失败。这就是为什么不得影响最终用户的程序行为。特别是,断言即使在特殊的程序条件下也不会失败。只是从来没有发生过。如果发生这种情况,程序员应该对此进行调整。

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:

# 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

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.


回答 7

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

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

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

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.

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

These loop invariants often can be represented with an assertion.


回答 8

是否存在性能问题?

  • 请记住“先使其工作,然后再使其快速工作”
    通常,几乎没有任何程序的百分比与其速度有关。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!

    这很便宜,但无论如何都会捕获大多数实际程序错误。

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 to be 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:

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

    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

    def mymethod(listOfTuples):
        assert(type(listOfTuples[0])==tuple)  # in fact _all_ must be tuples!
    

    which is cheap but will likely catch most of the actual program errors anyway.


回答 9

好吧,这是一个悬而未决的问题,我想谈谈两个方面:何时添加断言以及如何编写错误消息。

目的

向初学者解释它-断言是可能引发错误的语句,但是您不会抓住它们。而且通常不应该将它们提高,但是在现实生活中,无论如何它们有时都会得到提高。这是一种严重的情况,代码无法从中恢复,我们称之为“致命错误”。

接下来,它是出于“调试目的”,虽然正确,但听起来很不屑一顾。我更喜欢“声明不变式,永远不应该被违反”的表述,尽管它在不同的初学者中的工作方式有所不同……有些“只懂它”,而另一些要么找不到用处,要么替换正常的异常,甚至用它控制流程。

样式

在Python中,assert它是语句,而不是函数!(请记住assert(False, 'is true')不会提高。但是,请注意:

何时以及如何编写可选的“错误消息”?

此acually适用于单元测试框架,其通常具有许多专用的方法来做断言(assertTrue(condition)assertFalse(condition), assertEqual(actual, expected)等)。它们通常还提供一种对断言进行评论的方法。

在一次性代码中,您可以不显示错误消息。

在某些情况下,没有要添加的断言:

def dump(something):断言isinstance(something,Dumpable)#…

但是除此之外,一条消息对于与其他程序员(有时是代码的交互用户,例如在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 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! (remember assert(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:

assert meaningless_identifier <= MAGIC_NUMBER_XXX, 'meaningless_identifier is greater than MAGIC_NUMBER_XXX!!!'

write:

assert meaningless_identifier > MAGIC_NUMBER_XXX, 'reactor temperature above critical threshold'

or maybe even:

assert meaningless_identifier > MAGIC_NUMBER_XXX, f'reactor temperature({meaningless_identifier }) above critical threshold ({MAGIC_NUMBER_XXX})'

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:

assert a == b, 'a is not equal to b'
  • 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).


回答 10

assert异常的使用和引发都与沟通有关。

  • 断言是关于开发人员要解决的代码正确性的声明:代码中的断言将代码的正确性告知读者,有关正确代码必须满足的条件。在运行时失败的断言通知开发人员代码中存在需要修复的缺陷。

  • 异常是关于非典型情况的指示,这些非典型情况可能在运行时发生,但不能被手头的代码解决,请在此处处理的调用代码处解决。发生异常并不表示代码中存在错误。

最佳实践

因此,如果您将运行时发生的特定情况视为要通知开发人员的错误(“开发人员,此情况表明某个地方存在错误,请修复代码。”)然后断言。如果断言检查代码的输入参数,则通常应在输入参数违反条件时向文档添加代码具有“未定义行为”的文档。

如果不是这样的情况的发生并不是您眼中的错误的迹象,而是您认为应该由客户端代码处理的(可能很少见但)可能的情况,请引发异常。引发异常的情况应该是相应代码文档的一部分。

使用时是否存在性能问题? assert

断言的评估需要一些时间。不过,可以在编译时将其消除。但是,这会带来一些后果,请参见下文。

使用时是否存在代码维护问题 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.

Best practice

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.

Is there a performance […] issue with using assert

The evaluation of assertions takes some time. They can be eliminated at compile time, though. This has some consequences, however, see below.

Is there a […] code maintenance issue with using assert

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.


回答 11

声明将检查-1
.有效条件,
2.有效语句,
3.真实逻辑;
源代码。不会使整个项目失败,而是发出警报,指出源文件中不适当的内容。

在示例1中,由于变量’str’不为null。因此,不会引发任何断言或异常。

范例1:

#!/usr/bin/python

str = 'hello Python!'
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 Python!
FileName ..................... hello
FilePath ..................... C:/Python\hello.py

在示例2中,var’str’为null。因此,我们可以通过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

当我们不想调试并意识到源代码中的断言问题时。禁用优化标志

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:

#!/usr/bin/python

str = 'hello Python!'
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 Python!
FileName ..................... hello
FilePath ..................... C:/Python\hello.py

In example 2, var ‘str’ is null. So we are saving the user from going ahead of faulty program by assert statement.

Example 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

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


回答 12

在PTVS,PyCharm等IDE中,assert isinstance()可以使用Wing 语句为一些不清楚的对象启用代码完成功能。

In IDE’s such as PTVS, PyCharm, Wing assert isinstance() statements can be used to enable code completion for some unclear objects.


回答 13

对于它的价值,如果您要处理依靠assert其正常运行的代码,那么添加以下代码将确保启用断言:

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

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:

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