加注、尝试和断言之间的区别是什么?

我已经学习 Python 有一段时间了,raiseassert(我意识到它们都会让应用程序崩溃,不像 try 除外)非常相似,我看不出有什么情况可以在 try上使用 raiseassert

那么,raisetryassert之间的区别是什么呢?

85985 次浏览

声明 assert可以用来在运行时检查条件,但是 如果请求优化,则删除来自 Python:

assert condition, message

等同于:

if __debug__:
if not condition:
raise AssertionError(message)

其中 __debug__True是 Python 是 没有,启动时使用选项 -O

因此,assert condition, message的陈述类似于:

if not condition:
raise AssertionError(message)

因为两者都提高了 AssertionError。区别在于通过优化从执行的字节码中删除了 assert condition, message 可以(当这些优化被启用时——默认情况下它们不应用于 CPython)。相反,raise AssertionError(message)将在所有情况下执行。

因此,如果代码在任何情况下都应该检查并在检查失败时引发 AssertionError,那么就需要编写 if not condition: raise AssertionError

raise-引发异常。

引发异常 如果给定条件为(或不为)真。

执行一些 也许吧引发异常的代码,如果是这样,就捕获它。

异常是 Python (和其他一些语言)用来处理执行代码时出现的错误的。raise ExceptionName表示代码中有一个错误,并通过引发与该问题相关联的 Exception 来指定问题的类型。assert expression计算 expression,如果为 false 则引发 Exception。

try用于执行可能引发所期望的异常的代码。您可以“捕获”异常并在代码中处理它,而不是停止程序。

假设你有一本字典和一个列表。你想从字典里的列表中查找东西,直到你找到一个字典里没有的东西:

try:
for item in my_list:
print(my_dictionary[item])
except KeyError as e: #KeyError is the Exception raised when a key is not in a dictionary
print('There is no {} in the dictionary'.format(e.args[0]))

try/except块允许捕获和管理异常。异常可能由 raiseassert和大量错误触发,例如试图索引一个空列表。通常在检测到错误条件时使用 raiseassert类似,但只有满足条件时才会引发异常。

raiseassert有不同的理念。在代码中有许多“正常”错误,您可以检测并引发错误。也许网站不存在,或者参数值超出范围。

断言通常被用于“我发誓这不会发生”的问题,而这些问题似乎无论如何都会发生。它更像是运行时调试,而不是正常的运行时错误检测。如果使用 -O标志或从 .pyo文件而不是从 .pyc文件运行断言,则可以禁用断言,因此断言不应作为常规错误检测的一部分。

如果生产质量代码引发异常,那么就要找出您做错了什么。如果它提高了 AssertionError,你就有更大的问题了。

断言:

当你想基于某个条件“停止”脚本并返回一些有助于更快调试的东西时使用:

list_ = ["a","b","x"]
assert "x" in list_, "x is not in the list"
print("passed")
#>> prints passed


list_ = ["a","b","c"]
assert "x" in list_, "x is not in the list"
print("passed")
#>>
Traceback (most recent call last):
File "python", line 2, in <module>
AssertionError: x is not in the list

加注:

这种做法有用的两个原因是:

与 try 和 but 块一起使用。提出一个你选择的错误,可以像下面这样自定义,并不会停止脚本,如果你 passcontinue的脚本; 或者可以是预定义的错误 raise ValueError()

class Custom_error(BaseException):
pass


try:
print("hello")
raise Custom_error
print("world")
except Custom_error:
print("found it not stopping now")


print("im outside")


>> hello
>> found it not stopping now
>> im outside

注意到它没有停止吗? 我们可以使用 but 块中的 exit (1)来停止它。

还可以用于重新引发当前错误,将其传递到堆栈上,以查看是否有其他方法可以处理这个错误。

except SomeError, e:
if not can_handle(e):
raise
someone_take_care_of_it(e)

尝试/除积木:

完全按照你的想法去做,如果出现了错误,尝试一些事情,你会抓住它,然后按照你喜欢的方式去处理它。没有例子,因为上面有一个。

Assert 通常用于测试代码,以确保某些工作:

def test_bool():
assert True != False

当尝试时,引发和除了补丁异常处理,这是 Python 处理和传播错误的首选方法。

如果出现错误,大多数库和 Python 内置程序都会引发这种或那种类型的 Exception。通常在您自己的代码中,当您检测到某些错误时,也会想要引发异常。例如,您正在编写一个电子邮件地址验证器,并且希望在地址中不包含@符号时引发一个异常。你可以有这样的东西(这是玩具代码,不实际验证电子邮件像这样) :

def validate_email(address):
if not "@" in address:
raise ValueError("Email Addresses must contain @ sign")

然后,在代码的其他地方,您可以调用 valid_ email 函数,如果失败,将抛出一个异常。

try:
validate_email("Mynameisjoe.com")
except ValueError as ex:
print("We can do some special invalid input handling here, Like ask the user to retry the input")
finally:
close_my_connection()
print("Finally always runs whether we succeed or not. Good for clean up like shutting things down.")

要知道的重要事情是,当异常被引发时,它将沿调用堆栈向上传递,直到找到处理程序。如果它从来没有找到一个处理程序,那么它将崩溃与异常和堆栈跟踪程序。

有一件事你不想做,比如:

if __name__ == '__main__':
try:
print(1/0)
except Exception as ex:
pass

现在你无法知道为什么你的申请失败了。

你会经常看到这样的情况:

import logging
if __name__ == '__main__':
try:
print(1/0)
except Exception as ex:
logging.exception(ex)
raise

在这种情况下,由于没有参数而引发的重复引发相同的错误。通常在 web 代码中,你会看到类似的东西,不会重新引发异常,因为它会发送500错误给客户端,然后继续下一个请求,所以在这种情况下,你不希望程序结束。

断言

  • 只应用于调试目的
  • 虽然它们类似于“引发/异常”,但用途不同,因为它们可用于指出无法从中恢复程序错误的场景
  • 断言总是引发断言错误异常,它们是这样工作的:

语法 : assert_stmt ::= "assert" expression1 ["," expression2]

在执行时,它翻译成 :

if __debug__:
if not expression1:
raise AssertionError(expression2)
  • __debug__是一个内置标志,通常为 true,但是如果触发优化,则为 false,因此在启动 Python (或 CPython 中的 PYTHONOPTIMIZE env 变量)时,断言将使用-O 和-OO 标志禁用死代码 = > ,因此,不要依赖它们进行代码逻辑。
  • 不要将断言用于数据验证,因为前面有一点
  • 一个很好的断言用例 = > 使程序“爆炸”,如果程序的一些意外状态应该使它在所有情况下停止 = > 因此,在一个异常如果被捕将使程序完全退出的情况下。
  • 如果您有一个没有 bug 的程序,那么断言将/应该永远不会被触发,它们用作程序的健康检查
  • 在断言中使用数据结构(比如元组)作为表达式时要小心,因为对于非空值,断言总是被触发为 True = > ,因此要分解程序-例如: assert (<some_test>, 'warn string') = > 注意元组结构(错!)

检查: 在 CI 上捕捉伪 Python 断言 Dan Bader

加薪/例外情况

  • 它们的用途是处理程序逻辑处于异常状态但您知道从该状态恢复什么逻辑的情况
  • 当引发异常时,您可以使异常的类型与错误相适应(更好地控制语义值) ,并在稍后捕获它 = > ,这样您就可以创建多个您知道如何从中恢复并处理它们的异常类型
  • 它们是处理运行时错误的已知/预期场景的机制
  • 在使用 if 语句和每个场景引发验证异常时,对于数据验证非常有用

试试看

  • 只是编码异常处理的语法元素

顺便说一句,我强烈推荐这本书,由丹巴德(从 Realpython.com) “巨蟒戏法: 书”

当它们到位时,assertraise AssertionError之间没有区别,它们将被编译成完全相同的字节码:

import dis


def foo1(param):
assert param, "fail"


def foo2(param):
if not param:
raise AssertionError("fail")


dis.dis(foo1)
print("*" * 60)
dis.dis(foo2)

产出:

 4           0 LOAD_FAST                0 (param)
2 POP_JUMP_IF_TRUE        12
4 LOAD_GLOBAL              0 (AssertionError)
6 LOAD_CONST               1 ('fail')
8 CALL_FUNCTION            1
10 RAISE_VARARGS            1
>>   12 LOAD_CONST               0 (None)
14 RETURN_VALUE
************************************************************
7           0 LOAD_FAST                0 (param)
2 POP_JUMP_IF_TRUE        12


8           4 LOAD_GLOBAL              0 (AssertionError)
6 LOAD_CONST               1 ('fail')
8 CALL_FUNCTION            1
10 RAISE_VARARGS            1
>>   12 LOAD_CONST               0 (None)
14 RETURN_VALUE

但是请注意,当使用 -O-OO标志运行 Python 时,assert语句将被禁用,而对于任何提高语句则不是这种情况。

另一个 答案很好地解释了这种差异,但是许多人没有提到,在使用 -O 优化器标志时,assert语句被忽略。

使用类似简洁的 assert语法,使异常在使用 -O 时仍然有效,并且获得能够引发特定异常类型的好处的一个选项是定义一个效用函数,如下所示:

def raiseif(cond, msg="", exc=AssertionError):
if cond:
raise exc(msg)


raiseif(x != y, "x should equal y")

逻辑是从 assert倒置,但你可以很容易地改变,如果你想要的。

  • raise用于引发异常;
  • 如果给定的条件是 False,则使用 assert引发异常。