8. 异常


8. 异常

1. 异常概述

在 Python 中,异常是指在程序执行过程中出现的错误或意外情况。当这些情况发生时,正常的程序流程会被打断,Python 会创建一个异常对象。如果这个异常没有被妥善处理,程序就会终止并显示错误信息。

在程序运行过程中,由于各种原因(如用户输入错误、文件不存在、网络连接失败等)可能会导致程序出现错误。Python 使用异常对象来表示这些错误,一旦发生错误,就会抛出一个异常。例如,当你尝试对一个字符串和一个整数进行相加操作时,Python 会抛出 TypeError 异常:

a = "hello"
b = 123
result = a + b  # 这行代码会抛出 TypeError 异常

1. AssertionError

断言语句失败

在 Python 中,assert 语句用于调试程序,它允许你在代码中插入一些检查点。如果 assert 语句中的条件为 True,程序将继续正常执行;如果条件为 False,则会抛出一个 AssertionError 异常。

x = 5
assert x > 10, "x 应该大于 10"

2. AttributeError

尝试访问未知的对象属性

AttributeError 表示尝试访问一个对象并不存在的属性或方法时触发的错误。在 Python 里,一切皆对象,每个对象都有其自身的属性和方法。当你试图使用点号(.)去访问某个对象的属性或者调用其方法,而该对象并没有这个属性或方法时,就会抛出 AttributeError 异常。

3. IndexError

索引超出序列范围

IndexError 通常在尝试访问序列(如列表、元组、字符串等)中不存在的索引位置时抛出。在 Python 里,序列中的元素是通过索引来访问的,索引从 0 开始计数,即第一个元素的索引是 0,第二个元素的索引是 1,依此类推。当使用的索引超出了序列的有效范围时,就会触发 IndexError 异常。

4. KeyError

字典中查找一个不存在的关键字

KeyError 会在尝试访问字典中不存在的键时抛出。字典是一种无序的键值对集合,通过键来访问对应的值。当使用一个字典中不存在的键去获取值时,Python 就会触发 KeyError 异常。

5. OSError

操作系统产生的异常

OSError 是一个基类异常,用于表示与操作系统交互时发生的错误。当 Python 程序进行文件操作、网络操作、进程管理等和操作系统底层功能相关的操作时,如果出现问题,就可能抛出 OSError 或其子类异常。

6. NameError

尝试访问一个不存在的变量

NameError 通常在 Python 尝试访问一个未定义的变量、函数、类或模块名时抛出。在 Python 里,当你使用一个名称时,解释器会在当前的命名空间中查找该名称的定义,如果找不到对应的定义,就会触发 NameError 异常。

7. SyntaxError

Python的语法错误

SyntaxError 表示 Python 代码违反了 Python 语言的语法规则。当 Python 解释器在解析代码时,遇到不符合语法规范的语句,就会抛出 SyntaxError 异常,意味着代码的写法不符合 Python 规定的语法结构,解释器无法理解和执行这样的代码。

8. TypeError

不同类型间的无效操作

在 Python 中,不同的数据类型支持不同的操作和方法。当你对某个对象执行其不支持的操作,或者传递给函数的参数类型不符合函数的要求时,Python 解释器会抛出 TypeError,提示你使用了不恰当的数据类型。

9. ZeroDivisionError

除数为零异常

在数学运算里,除数为零是没有意义的,所以 Python 解释器在检测到除法运算中的除数为零这种情况时,会抛出 ZeroDivisionError 异常来阻止程序进行无效的计算。

10. 常见的标准异常类表

异常类名 描述
BaseException 所有异常的基类
系统退出异常
SystemExit sys.exit() 函数引发,用于退出 Python 解释器
KeyboardInterrupt 用户按下 Ctrl+CCtrl+Break 时引发,表示用户中断程序执行
GeneratorExit 当生成器的 close() 方法被调用时引发
一般异常
Exception 所有内置的非系统退出异常的基类,自定义异常通常也继承自它
标准错误异常
StopIteration 当迭代器的 __next__() 方法没有更多元素可返回时引发
StopAsyncIteration 当异步迭代器的 __anext__() 方法没有更多元素可返回时引发
ArithmeticError 所有数值计算错误的基类
- FloatingPointError 浮点计算错误,通常在不常见的浮点异常情况下触发
- OverflowError 数值运算结果超出了最大表示范围
- ZeroDivisionError 当除法或取模运算的除数为零时引发
AssertionError assert 语句的条件为 False 时引发
AttributeError 当试图访问对象不存在的属性时引发
BufferError 当与缓冲区相关的操作失败时引发
EOFError 当输入函数(如 input())遇到文件结束符(EOF)时引发
ImportError import 语句无法找到模块或在 from ... import 中无法找到指定的名称时引发
- ModuleNotFoundError ImportError 的子类,当无法找到指定模块时引发
LookupError 所有查找错误的基类
- IndexError 当使用超出序列(如列表、元组)索引范围的索引时引发
- KeyError 当使用字典中不存在的键时引发
MemoryError 当程序耗尽内存时引发
NameError 当使用未定义的变量或函数名时引发
- UnboundLocalError 当在函数内部使用未赋值的局部变量时引发
OSError 操作系统相关错误的基类
- BlockingIOError 当操作会阻塞时引发,通常与非阻塞 I/O 操作相关
- ChildProcessError 当子进程操作失败时引发
- ConnectionError 所有与连接相关错误的基类
- - BrokenPipeError 当试图向一个已关闭的管道或套接字写入数据时引发
- - ConnectionAbortedError 连接被对方中止时引发
- - ConnectionRefusedError 连接被对方拒绝时引发
- - ConnectionResetError 连接被对方重置时引发
- FileExistsError 当试图创建已存在的文件或目录时引发
- FileNotFoundError 当试图访问不存在的文件或目录时引发
- InterruptedError 系统调用被中断时引发
- IsADirectoryError 当对目录执行不适合的操作(如尝试以文件方式打开目录)时引发
- NotADirectoryError 当操作要求对象是目录,但实际不是时引发
- PermissionError 当操作没有足够的权限时引发
- ProcessLookupError 当试图操作不存在的进程时引发
- TimeoutError 当操作超时时引发
ReferenceError 当使用弱引用访问已经被垃圾回收的对象时引发
RuntimeError 当出现无法归类到其他异常类别的错误时引发
- NotImplementedError 当在抽象方法或需要子类实现的方法中没有提供具体实现时引发
- RecursionError 递归调用超出最大递归深度时引发
SyntaxError 当代码存在语法错误时引发
- IndentationError 缩进错误,是 SyntaxError 的子类
- - TabError 当混合使用制表符和空格进行缩进时引发
SystemError 解释器内部错误,通常表示 Python 解释器有问题
TypeError 当操作或函数应用于不适当类型的对象时引发
ValueError 当函数接收到正确类型但值不合适的参数时引发
- UnicodeError 所有与 Unicode 相关错误的基类
- - UnicodeDecodeError 当 Unicode 解码失败时引发
- - UnicodeEncodeError 当 Unicode 编码失败时引发
- - UnicodeTranslateError 当 Unicode 转换失败时引发
Warning 所有警告的基类
- DeprecationWarning 当使用已弃用的功能时发出的警告
- PendingDeprecationWarning 表示将来某个功能可能会被弃用的警告
- SyntaxWarning 关于可疑语法的警告
- RuntimeWarning 关于运行时行为的警告
- FutureWarning 关于将来语义变化的警告
- UserWarning 用户代码中发出的警告
- ImportWarning 关于导入模块时的警告
- UnicodeWarning 关于 Unicode 相关操作的警告
- BytesWarning 关于字节和字节数组操作的警告
- ResourceWarning 关于资源使用的警告,通常在资源未正确关闭时发出

2. 捕获处理异常

1. 简单异常捕获

try:
    检测范围
except Exception [as e]:
    出现异常后的处理代码

[as e]是可选内容, as e 部分用于将捕获到的异常对象赋值给一个变量(这里是 e),方便你在 except 代码块中使用该异常对象的相关信息。

示例:

try:
    # 可能会抛出异常的代码块
    result = 10 / 0  # 这里会抛出 ZeroDivisionError 异常
except ZeroDivisionError:
    # 处理 ZeroDivisionError 异常的代码块
    print("除数不能为零!")

在上述代码中,try 块中的代码是可能会引发异常的部分。如果 try 块中的代码执行时抛出了 ZeroDivisionError 异常,程序会立即跳转到对应的 except 块中执行处理代码。

2. 捕获多种类型异常

语法结构:

try:
    检测范围
except Exception1:
    出现异常后的处理代码
except Exception2:
    出现异常后的处理代码
······

示例:

try:
    num = int(input("请输入一个整数:"))
    result = 10 / num
    print(result)
except ValueError:
    print("输入的不是有效的整数!")
except ZeroDivisionError:
    print("除数不能为零!")

在这个例子中,try 块中的代码首先尝试将用户输入转换为整数,如果输入不是有效的整数,会抛出 ValueError 异常,程序会跳转到第一个 except 块处理;如果输入是 0,进行除法运算时会抛出 ZeroDivisionError 异常,程序会跳转到第二个 except 块处理。

3. 捕获全部异常

可以使用一个不带异常类型的 except 子句来捕获所有异常,但这种做法不建议经常使用,因为它会掩盖一些潜在的问题:

try:
    num = int(input("请输入一个整数:"))
    result = 10 / num
    print(result)
except:
    print("发生了一个错误!")

4. else 子句

try - except 语句可以包含一个 else 子句,当 try 块中没有抛出任何异常时,else 子句中的代码会被执行:

try:
    num = int(input("请输入一个整数:"))
    result = 10 / num
except ValueError:
    print("输入的不是有效的整数!")
except ZeroDivisionError:
    print("除数不能为零!")
else:
    print("计算结果:", result)

5. finally 子句

finally 子句是可选的,无论 try 块中是否抛出异常,finally 子句中的代码都会被执行。通常用于释放资源,如关闭文件、关闭数据库连接等:

try:
    num = int(input("请输入一个整数:"))
    result = 10 / num
except ValueError:
    print("输入的不是有效的整数!")
except ZeroDivisionError:
    print("除数不能为零!")
else:
    print("计算结果:", result)
finally:
    print("程序执行结束。")

3. 异常抛出

如果希望主动抛出异常,可以先创建一个Exception对象或其子类对象,然后使用raise关键字手动抛出异常。

raise 语句用于手动抛出异常。可以抛出内置的异常类型,也可以抛出自定义的异常类型:

def divide(a, b):
    if b == 0:
        raise ZeroDivisionError("除数不能为零!")
    return a / b

try:
    result = divide(10, 0)
except ZeroDivisionError as e:
    print(e)

1. 直接抛出一个指定类型的异常

  • 格式raise 异常类型
  • 示例
# 直接抛出一个 ValueError 异常
raise ValueError

在这个示例中,当执行到 raise ValueError 语句时,程序会立即抛出一个 ValueError 异常,后续代码将不再执行,除非这个异常被捕获处理。

2. 抛出异常并附带错误信息

  • 格式raise 异常类型("错误信息")
  • 示例
try:
    age = -1
    if age < 0:
        # 抛出 ValueError 异常并附带错误信息
        raise ValueError("年龄不能为负数")
except ValueError as e:
    print(f"捕获到异常: {e}")

在上述代码中,当 age 为负数时,会抛出一个带有特定错误信息的 ValueError 异常。在 except 块中,可以通过异常对象获取这个错误信息并进行处理。

4. 自定义异常

示例:

class MyCustomError(Exception):
    pass

def check_value(value):
    if value < 0:
        raise MyCustomError("值不能为负数!")
    return value

try:
    result = check_value(-5)
except MyCustomError as e:
    print(e)

1. 定义异常类

首先,创建一个继承自 Exception 的新类,这个类就是你自定义的异常类型。你可以在类中添加额外的属性和方法,但通常只需要继承 Exception 类并定义类名即可。

class MyCustomError(Exception):
    pass

在上述代码中,MyCustomError 是一个自定义的异常类,它继承自 Exception 类。pass 语句表示该类目前没有额外的代码实现。

2. 抛出自定义异常

在程序中,当满足特定条件时,可以使用 raise 关键字抛出你自定义的异常。

def check_age(age):
    if age < 0:
        raise MyCustomError("年龄不能为负数")
    return age

try:
    age = check_age(-5)
except MyCustomError as e:
    print(f"捕获到自定义异常: {e}")

在这个示例中,check_age 函数用于检查年龄是否为负数。如果年龄为负数,就抛出 MyCustomError 异常,并附带错误信息 "年龄不能为负数"。在 try-except 块中,捕获这个自定义异常并打印错误信息。

3. 带参数的自定义异常

你可以在自定义异常类中添加 __init__ 方法,以便在抛出异常时传递更多的参数。

class DatabaseError(Exception):
    def __init__(self, error_code, message):
        self.error_code = error_code
        self.message = message
        super().__init__(f"错误代码: {error_code}, 错误信息: {message}")

try:
    # 模拟数据库操作出错
    raise DatabaseError(500, "数据库连接失败")
except DatabaseError as e:
    print(f"捕获到数据库异常: {e}")
    print(f"错误代码: {e.error_code}")
    print(f"错误信息: {e.message}")

在这个例子中,DatabaseError 是一个自定义的异常类,它的 __init__ 方法接收 error_codemessage 两个参数,并将它们存储为实例属性。通过 super().__init__ 方法将错误信息传递给父类 Exception,这样在捕获异常时可以获取到完整的错误描述。

5. 异常和错误的区别

  • 异常:异常是程序运行时出现的非正常情况,但这种情况是可以被程序捕获和处理的。通常是由于一些可预见的错误输入、外部资源问题等导致的,程序可以通过异常处理机制来避免崩溃,继续执行后续代码。
  • 错误:错误通常指的是 Python 解释器在执行过程中遇到的严重问题,一般是由系统层面、解释器本身或者 Python 运行环境等不可控因素引起的,这些问题往往无法通过程序代码进行处理,会导致程序无法正常运行甚至崩溃。

0 条评论

发表评论

暂无评论,欢迎发表您的观点!