如何在Python中处理异常以及抛出异常?

您所在的位置:网站首页 python里的异常处理 如何在Python中处理异常以及抛出异常?

如何在Python中处理异常以及抛出异常?

2023-03-17 01:07| 来源: 网络整理| 查看: 265

当我们用Python编程的时候,常常会出现很多错误,大多数是语法错误,当然也有一些语义错误。例如,我就经常忘记在if语句后面加冒号,然后运行的时候就会报错如下所示。

>>> if 5 % 2 == 1 File "", line 1 if 5 % 2 == 1 ^ SyntaxError: invalid syntax

如果我们使用Pycharm等IDE来写代码的时候,这种低级的语法错误会被IDE用红色波浪线标注出来,会比较方便我们解决这些错误。

但是,除了这些错误,我们写Python代码的时候也很可能包含一些逻辑上的错误,例如TypeError就是我们经常遇到的一种错误,它表示数据类型不匹配。

>>> "This year is " + 2020 + "." Traceback (most recent call last): File "", line 1, in TypeError: can only concatenate str (not "int") to str

例如上述代码从字面上理解是字符串的拼接,但是由于2020它是int整型,而不是一个字符串,不能应用于字符串的拼接。所以产生了TypeError异常。这种在Python代码执行过程中发生的非语法错误被称为异常。接下来,本文将介绍如何优雅地在Python中处理异常以及抛出异常。

异常处理的基本形式

处理异常的标准方法就是使用try...except语句。这一点其实比较类似于Java中的try...catch语句,事实上,大部分语言都有类似的捕捉异常的方法。

通常来说,可能产生异常的代码应该被try语句囊括进去,如果报异常的就会立即停止try语句中的剩余代码,并执行except语句中的代码。

我们可以看一个简单示例

>>> # Declare a function that can handle ZeroDivisionError >>> def divide_twelve(number): ... try: ... print(f"Result: {12/number}") ... except ZeroDivisionError: ... print("You can't divide 12 by zero.") ... >>> # Use the function >>> divide_twelve(6) Result: 2.0 >>> divide_twelve(0) You can't divide 12 by zero.

我们都知道0不能做分母,因此在上述代码中,当0为分母时就产生了一个异常。于是就执行except语句中的代码了。

为什么要处理异常?

为什么要处理异常?因为未经处理的异常会直接中断Python程序的运行,举个例子,假如我们部署一个Python写的网站,其中一个用户的操作触发了某个Bug,导致程序产生了异常,Web服务就会直接被终止,所有用户都不能访问使用了,这显然会造成巨大损失。

>>> # Define a function without handling >>> def division_no_handle(x): ... print(f"Result: {20/x}") ... print("division_no_handle completes running") ... >>> # Define a function with handling >>> def division_handle(x): ... try: ... print(f"Result: {20/x}") ... except ZeroDivisionError: ... print("You can't divide a number with zero.") ... print("division_handle completes running") ... >>> # Call the functions >>> division_handle(0) You can't divide a number with zero. division_handle completes running >>> division_no_handle(0) Traceback (most recent call last): File "", line 1, in File "", line 2, in division_no_handle ZeroDivisionError: division by zero

通过上面的代码,我们也可以发现,当我们调用division_handle(0)时,由于异常得到了处理,程序还能继续运行,但是,当我们调用没有处理异常的division_no_handle(0)时,程序就直接中断了。

变量分配

我们可以将异常赋值给一个变量,从而进一步了解该异常的相关信息,方便对该异常进行处理。例如在下面代码中,我们可以将处理后的TypeError异常赋值给变量e,并将其打印出来。

>>> # A function that show error's detail >>> def concat_messages(x, y): ... try: ... print(f"{x + y}") ... except TypeError as e: ... print(f"Argument Error: {e}") ... >>> # Call the function >>> concat_messages("Hello, ", 2020) Argument Error: can only concatenate str (not "int") to str

当然,我们也可以一次捕捉多个异常,我们将可能的异常包装在一个元组中,如下面的代码片段中所示。当我们调用函数时,我们分别触发ValueError和ZeroDivisionError。可以看到两种异常都被捕获到了

>>> # A function that handles multiple exceptions >>> def divide_six(number): ... try: ... formatted_number = int(number) ... result = 6/formatted_number ... except (ValueError, ZeroDivisionError) as e: ... print(f"Error {type(e)}: {e}") ... >>> # Use the function >>> divide_six("six") Error : invalid literal for int() with base 10: 'six' >>> divide_six(0) Error : division by zero异常处理语句

1,多异常语句

事实上,我们可以通过多个except子句来处理多个异常,每个子句都处理一些特定的异常。如下所示。

>>> # A function that has multiple except clauses >>> def divide_six(number): ... try: ... formatted_number = int(number) ... result = 6/formatted_number ... except ValueError: ... print("This is a ValueError") ... except ZeroDivisionError: ... print("This is a ZeroDivisionError") ... >>> # Use the function >>> divide_six("six") This is a ValueError >>> divide_six(0) This is a ZeroDivisionError

2,else语句

我们也可以在try ... except代码块中使用else子句。不过需要注意的是,else子句需要出现在except子句之后。只有当try子句完成而没有引发任何异常时,else子句中的代码才会运行。如下例所示。

>>> # A function that has an else clause >>> def divide_eight(number): ... try: ... result = 8/number ... except: ... print("divide_eight has an error") ... else: ... print(f"Result: {result}") ... >>> # Use the function >>> divide_eight(0) divide_eight has an error >>> divide_eight(4) Result: 2.0

3,finally语句

finally子句放在块的最末尾,将在整个try…except块完成之后运行。只需要记住,无论是否产生异常,finally子句都会被执行就可以了,写法和else语句是一样的。

抛出异常

前面我们学习了使用try ... except块来处理Python中异常,接下来我们来看看抛出异常(产生)异常的基本形式。

>>> # Raise exceptions >>> raise Exception Traceback (most recent call last): File "", line 1, in Exception >>> raise NameError Traceback (most recent call last): File "", line 1, in NameError >>> raise ValueError() Traceback (most recent call last): File "", line 1, in ValueError

如上所示,Python中使用raise关键字(Java中是throw关键字)后面跟上异常类(例如Exception,NameError)的方式来抛出异常。我们还可以使用异常类构造函数来创建实例,例如ValueError()。这两种用法没有区别,前者只是后者使用构造函数的语法糖。

1,自定义异常信息

我们还可以提供有关我们提出的异常的其他信息。最简单的方法是使用异常类构造函数,并包含适用的错误消息来创建实例。如下所示:

>>> raise ValueError("You can't divide something with zero.") Traceback (most recent call last): File "", line 1, in ValueError: You can't divide something with zero. >>> raise NameError("It's silly to make this mistake.") Traceback (most recent call last): File "", line 1, in NameError: It's silly to make this mistake.

2,再抛出异常如图所示,所有异常都会在被捕获时进行处理。但是,有可能我们可以重新抛出异常并将异常传递给外部范围,以查看是否可以处理该异常。

当我们编写涉及嵌套结构的复杂代码时(例如,一个函数调用另一个函数,该函数可能会调用另一个函数),此功能会更加有用。通过再抛出异常,我们可以决定在哪里处理特定异常。当然,根据具体情况确定处理特定异常的确切位置。我们先来看一些代码:

>>> # Define two functions with one calling the other >>> def cast_number(number_text, to_raise): ... try: ... int(number_text) ... except: ... print("Failed to cast") ... if to_raise: ... print("Re-raise the exception") ... raise ... >>> def run_cast_number(number_text, to_raise): ... try: ... cast_number(number_text, to_raise) ... except: ... print("Handled in run_cast_number") ... >>> # Use the functions >>> run_cast_number("six", False) Failed to cast >>> run_cast_number("six", True) Failed to cast Re-raise the exception Handled in run_cast_number

在上面的代码中,我们有两个函数,其中run_cast_number调用另一个函数cast_number。我们使用字符串两次调用该函数,这两个函数都会导致异常,因此将显示消息“ Fasted to cast”,因为该异常是在cast_number函数中处理的。但是,第二次调用函数,我们要求cast_number函数重新引发异常,以便except子句在run_cast_number函数中运行。

3,用户自定义异常

在许多情况下,我们可以使用内置的异常来帮助我们在项目中引发和处理异常。但是,Python使我们可以灵活地创建自己的自定义异常类。也就是说,我们需要将一个类声明为内置Exception类的子类。

>>> # Define a custom exception class >>> class FileExtensionError(Exception): ... def __init__(self, filename, desired_ext): ... self.filename = filename ... self.desired_ext = desired_ext ... def __str__(self): ... return f"File {self.filename} should have the extension: {self.desired_ext}." ... >>> # Raise custom exceptions >>> raise FileExtensionError Traceback (most recent call last): File "", line 1, in TypeError: __init__() missing 2 required positional arguments: 'filename' and 'desired_ext' >>> raise FileExtensionError("test.xls", "csv") Traceback (most recent call last): File "", line 1, in __main__.FileExtensionError: File test.xls should have the extension: csv.

如上所示,我们创建了一个名为FileExtensionError的自定义异常类。当我们抛出这个异常时,仅使用类名是行不通的。相反,我们应该通过为构造方法设置两个位置参数来实例化此异常。通过实现__str__方法,我们可以看到自定义异常消息。换句话说,异常消息是通过调用str()函数生成的。

4, 何时应该抛出异常

一般来说,当代码可能会在某些情况下会报错无法执行时,就应该抛出异常。



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3