跟着我学Python进阶篇:04. 错误和异常

往期文章

跟着我学Python基础篇:01.初露端倪
跟着我学Python基础篇:02.数字与字符串编程
跟着我学Python基础篇:03.选择结构
跟着我学Python基础篇:04.循环
跟着我学Python基础篇:05.函数
跟着我学Python基础篇:06.列表
跟着我学Python基础篇:07.文本
跟着我学Python基础篇:08.集合和字典


跟着我学Python进阶篇:01.试用Python完成一些简单问题
跟着我学Python进阶篇:02.面向对象(上)
跟着我学Python进阶篇:03.面向对象(下)


目录

  • 往期文章
  • 1. 错误和异常的简介
    • 1.1 异常
    • 1.2 Python常见异常
  • 2. 捕获异常
    • 2.1 try-expect语句
    • 2.2 获取异常提示信息
    • 2.3 捕获多个异常
    • 2.4 捕获所有异常
    • 2.5 else语句
    • 2.6 finally语句
  • 3. 抛出异常
    • 3.1 raise语句
    • 3.2 异常的传递
    • 3.3 assert断言语句
  • 4. 自定义异常
  • 5. with语句处理异常
    • 5.1 with语句
    • 5.2 上下文管理器
    • 5.3 自定义上下文管理器

1. 错误和异常的简介

程序中的错误分为逻辑错误和语法错误,语法错误是指软件的编写不符合Python语言的语法规定,导致无法被解释器解释或者编译器编译。这些错误必须修正,程序才能运行。语法错误的类型是SyntaxError。程序运行以后出现的错误是逻辑错误,逻辑错误可能是由于外界条件允许的,也可能是程序本身设计不严谨导致的。不管哪种错误,只要被Python检测到,都会发生异常。

1.1 异常

在程序运行期间检测到的错误称之为异常,如果异常不被处理,默认的方式是程序崩溃,并给出错误提示。

1.2 Python常见异常

AttributeError:尝试访问未定义的属性或方法时引发的异常。
FileNotFoundError:尝试打开不存在的文件时引发的异常。
IndexError:尝试访问列表、元组或字符串中不存在的索引时引发的异常。
KeyError:尝试访问字典中不存在的键时引发的异常。
NameError:尝试访问未声明或未初始化的变量时引发的异常。
TypeError:操作或函数应用于不适当类型的对象时引发的异常。
ValueError:传递给函数的参数具有正确的类型,但具有不合适的值时引发的异常。
ZeroDivisionError:除以零时引发的异常。

2. 捕获异常

Python中使用try-expect语句处理异常,其中try语句检测异常,except语句捕获异常。

2.1 try-expect语句

跟着我学Python进阶篇:04. 错误和异常_第1张图片

首先,“try” 块中的代码会被执行。这是可能引发异常的代码块。
如果在 “try” 块中的代码执行期间没有发生任何异常,那么 “except” 块将被跳过,程序将继续执行后续的代码。
如果在 “try” 块中的代码执行期间引发了一个异常,那么与该异常类型匹配的第一个 “except” 块将被执行。如果有多个 “except” 块,并且匹配多个异常类型,只有第一个匹配的块将被执行,其他的块将被忽略。
异常被捕获后,与异常相关的 “except” 块中的代码将被执行。这是你可以在其中处理和恢复异常的地方。
如果没有与异常类型匹配的 “except” 块,或者异常类型没有被指定,那么该异常将传递给上一层的调用堆栈,直到找到能够处理该异常的 “except” 块或者程序终止。

2.2 获取异常提示信息

有些异常可以在程序中纠正,有些不能。一般程序中会维护一个日志,记录程序中出现的所有异常,此时可以使用as获取系统反馈的具体信息。

例如:

try:
    print("我的年龄是"+18)
    print("我叫富豪")
except TypeError as error:
    print("错误信息:")
    print(error)

跟着我学Python进阶篇:04. 错误和异常_第2张图片

2.3 捕获多个异常

一个代码可能出现多个异常,可以将多个特定异常组成一个元祖放在一个except语句后处理,也可以多个except字句联合使用。

try:
    print(abc)
    print(2/0)
except(NameError,ZeroDivisionError) as error:
    print(error)

跟着我学Python进阶篇:04. 错误和异常_第3张图片
同时捕获多个异常,只能使用一种方式进行处理,如果希望对不同的异常采用不同的处理方式,可以使用多个except字句,可以使用下面的写法:

try:
    print(2/0)
    print(abc)
except NameError as error:
    print(error)
except ZeroDivisionError:
    print("不能使用0作为除数")

注:Python2中把多个异常用逗号隔开,Python3需要将多个异常元祖组织起来,也就是多个圆括号。

2.4 捕获所有异常

在Python中捕获所有异常有两种方式,一种使用Exception,由于Exception是所有异常的基类,所以可以包含所有异常。

try:
    demo_list=[1,2,3,4]
    print(demo_list[5])
except Exception as error:
    print(error)
    print("错误")
    

跟着我学Python进阶篇:04. 错误和异常_第4张图片
另一种方式是使用except,如:

try:
    demo_list=[1,2,3,4]
    print(demo_list[5])
except:
    print("错误")

跟着我学Python进阶篇:04. 错误和异常_第5张图片
在except后缺异常类型,也会将所有异常都捕获。不过这种方式并不推荐,一是不能获取错误的具体信息,二是它还会捕获我们并不想捕获的异常,比如用户终止执行的Ctrl C操作,以及用sys.exit()函数程序终止操作。

异常处理的主要目的是防止因外部环境变化导致程序无法控制的错误,而不应该用于处理程序设计错误,将所有代码用try语句包含是不正确的。
捕获异常之后要进行相应的处理,如果异常之后用pass直接忽略掉,会隐藏掉程序出错的原因,不利于程序的运行和维护,也是不推荐的。

2.5 else语句

else语句可以和try-expect配合使用:
try:
可能会引发异常的代码块

except ExceptionType1:
处理 ExceptionType1 类型的异常的代码块

except ExceptionType2:
处理 ExceptionType2 类型的异常的代码块

else:
没有发生异常时执行的代码块

2.6 finally语句

finally语句与try语句联合使用,表示无论try语句是否出错都会执行语句。
try:
# 可能会引发异常的代码块
# …
finally:
# 无论是否发生异常,都会执行的代码块
# …

3. 抛出异常

如果捕获到的异常在本级无法处理,或者不应由本级处理,也可以将异常抛出,交给上一级代码处理。

3.1 raise语句

raise语句用于抛出特定的异常。

  1. 创建类的实例对象,引发异常:
    raise 异常类名
  2. 引发异常实例对象对应的异常
    raise 异常类实例对象
  3. 重新引发刚才发生的异常
    raise

在上述格式中,第一种和第二种是对等的,都会引发指定异常类的实例,但是第一种形式隐式地创建了异常类的实例,而第二种形式是最常见的,直接提供一个实例,第三种用于重新引发刚刚发生的异常。

下面通过实例来对raise语句进行介绍:

  1. 使用类名引发异常

当raise语句指定异常类名时,会创建该类的实例对象,然后引发异常。

raise IndexError

跟着我学Python进阶篇:04. 错误和异常_第6张图片

  1. 使用异常类的实例引发异常
    通过显式地创建异常类的实例,直接使用该实例对象来引发异常。
index=IndexError()
raise index

跟着我学Python进阶篇:04. 错误和异常_第7张图片

  1. 传递异常
    不带任何参数的raise语句,可以再次引发刚刚发生过的异常,作用就是向外传递异常。
try:
    raise IndexError
except:
    print("出错了")
    raise

跟着我学Python进阶篇:04. 错误和异常_第8张图片

  1. 指定异常的描述信息
    当使用raise语句抛出异常时,还能给异常类指定描述信息。
raise IndexError("索引超出下标范围")

跟着我学Python进阶篇:04. 错误和异常_第9张图片

  1. 异常引发异常
    如果要在异常中抛出另一个异常,可以使用raise from语句实现。

跟着我学Python进阶篇:04. 错误和异常_第10张图片

3.2 异常的传递

如果异常没有被处理,默认情况将会上传给上层代码,逐级向上传递,如果最上层的代码没有处理,则会使用系统默认方式。

3.3 assert断言语句

assert语句是一种用于调试和测试的Python语句。它用于检查一个条件是否为真,如果为假,则会引发一个AssertionError异常。

assert语句的语法如下:
assert condition, message

4. 自定义异常

自定义异常类需要创建一个类,让他继承自Exception类或其子类即可。

class MyCustomException(Exception):
    def __init__(self, message):
        super().__init__(message)
        self.error_code = 100

    def log_error(self):
        # 自定义异常类的方法
        print("Logging error: ", self.__str__())

try:
    raise MyCustomException("Something went wrong!")
except MyCustomException as e:
    print("Exception occurred:", e)
    e.log_error()

5. with语句处理异常

with语句支持创建资源、抛出异常、释放资源等操作。

5.1 with语句

with语句适用于对资源进行访问的场合,无论资源使用过程中是否发生异常,都会执行必要的释放资源的操作。比如文件使用后自动关闭,线程中锁的自动获取和释放。
with语句的语法格式如下:
with 上下文表达式 [as 资源对象]:
对对象的操作

上下文表达式返回一个上下文管理对象,如果指定as子句,该对象并不赋值给as子句中的资源对象,而是将上下文管理器的_enter()_方法赋值给资源对象,资源对象可以是单个变量也可以是元祖。

例如,对with语句操作文件对象的示例如下:

with open(‘test.txt’) as file:
for aline in file:
print(aline)

with语句从Python2.5 开始引入(需通过from_future_import with_statement导入),从Python2.6开始可以缺省。

不是所有对象都可以使用with语句,只有支持上下文管理协议的对象才可以。

5.2 上下文管理器

上下文管理协议与with有关的概念包括:

  1. 上下文管理协议(Context Management Protocal)
    enter(): 当进入with语句块时,会调用该方法。它负责返回一个值,通常是对象本身或与上下文相关的资源。
    exit(exc_type, exc_value, traceback): 当离开with语句块时,无论是正常离开还是发生异常,都会调用该方法。它负责处理上下文退出时的清理工作,关闭资源等。

  2. 上下文管理器
    支持上下文管理协议的对象就是上下文管理器,这种对象实现了_enter_()和_exit_()方法,使用with语句即可调用上下文管理器,它负责建立运行时上下文。

  3. 运行时上下文
    由上下文管理器创建,通过上下文管理器的_enter_()和_exit_()方法实现。

  4. 上下文表达式
    with语句在with关键字后出现的表达式,该表达式要返回一个支持上下文管理协议的对象,也就是返回一个上下文管理器。

  5. 语句体
    由with语句包括起来的包括块,表示对资源对象的操作。在执行语句之前会执行上下文管理器的_enter_()方法,执行之后调用_exit_()方法。

了解了上下文管理器就理解了with语句执行过程了:

当执行到with语句时,首先调用上下文管理器对象的_enter_()方法,该方法负责获取资源并返回一个值。
进入with语句块,执行其中的代码。
如果在with语句块中发生异常,异常会被捕获。
无论是否发生异常,都会调用上下文管理器对象的_exit_(exc_type, exc_value, traceback)方法。如果没有发生异常,这三个参数都是None;如果有异常,这三个参数分别表示异常类型、异常实例和追溯信息。

5.3 自定义上下文管理器

要自定义上下文管理器,需要创建一个类并实现上下文管理协议的方法_enter_()和_exit_()。

class MyContext:
    def __enter__(self):
        print("Entering context")
        # 返回要在上下文中使用的对象或资源
        return "Hello, world!"

    def __exit__(self, exc_type, exc_value, traceback):
        print("Exiting context")
        # 处理上下文退出时的清理工作
        if exc_type:
            print("Exception occurred:", exc_type, exc_value)

# 使用自定义的上下文管理器
with MyContext() as context:
    print(context)
    # 在上下文中执行一些操作

# 上下文外部继续执行其他代码
print("Outside context")

在上面的例子中,我们定义了一个名为MyContext的上下文管理器类。它实现了__enter_()和 exit()方法。

在_enter__()方法中,我们可以执行一些准备工作,并返回一个对象或资源,该对象或资源将在with语句块中使用。在这个示例中,我们返回了一个简单的字符串。

在_exit_()方法中,我们可以处理上下文退出时的清理工作,例如关闭文件、释放资源等。如果在with语句块中发生异常,异常信息会传递给_exit__()方法的参数。

然后,我们使用with语句创建了一个MyContext对象,并将其赋值给context变量。在`with语句块中,我们打印了context的值,并执行了一些操作。

当离开with语句块时,无论正常离开还是发生异常,都会自动调用_exit_()方法,进行清理工作。在这个示例中,我们打印了退出上下文的信息。

最后,在上下文外部继续执行其他代码,打印了"Outside context"。

你可能感兴趣的:(Python,python)