在 Python 编程中,错误异常是两个容易混淆但非常重要的概念。正确理解并处理它们,能让你的程序更加健壮、友好。本文将从基础概念讲起,逐步深入异常处理的各种用法,最后带你实现自定义异常,每个知识点都配有定义和代码示例

一、错误 vs. 异常(定义 + 对比表)

1.1 定义

  • 错误(Error):指代码语法本身有错误,解释器无法执行。这类问题无法通过异常处理机制解决,必须手动修正代码。

  • 异常(Exception):代码语法正确,但在执行过程中出现了问题(如除零、索引越界)。这类问题可以通过 try...except 机制捕获并处理,避免程序崩溃。

1.2 对比表

特性 错误 异常
本质 代码语法错误,解释器无法执行 语法正确,但运行时出现问题
能否处理 无法通过异常处理机制解决 可以通过 try...except 捕获处理
例子 缺少冒号、缩进错误、括号不匹配 除零错误、索引越界、键不存在

1.3 代码示例

错误示例(语法错误)

​
# 下面的代码会报 SyntaxError,因为 if 语句后面缺少冒号
age = 18
if age >= 18   # 缺少冒号
    print('chengnian')

​

异常示例(运行时异常)

# AttributeError:对象没有该属性
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p1 = Person('张三', 18)
print(p1.name)   # 正常输出:张三
print(p1.height) # AttributeError: 'Person' object has no attribute 'height'

# IndexError:列表索引超出范围
l1 = [1, 2, 3]
print(l1[5])  # IndexError: list index out of range

# NameError:变量未定义
print(Name)   # NameError: name 'Name' is not defined

# KeyError:字典键不存在
s1 = {'name':'zhangsan', 'age':18}
print(s1['height'])  # KeyError: 'height'

二、为什么要处理异常?(定义)

定义:当程序运行时出现异常且未被处理时,异常之后的代码将无法执行,程序会直接终止。异常处理的目的是捕获异常,然后根据业务逻辑决定如何处理(如重试、记录日志、给出友好提示),而不是让异常消失。这样可以提高程序的健壮性和用户体验。

2.1 未处理异常的后果(代码示例)

# 没有进行异常处理
print('欢迎使用本程序')
a = int(input('请输入第一个数字:'))
b = int(input('请输入第二个数字:'))
res = a / b
print(f'{a}除以{b}的结果为:{a/b}')
print('这是后续的其他的逻辑1')   # 如果上面出现异常,这行不会执行
print('这是后续的其他的逻辑2')


运行结果(当用户输入 10 和 0 时):
欢迎使用本程序
请输入第一个数字:10
请输入第二个数字:0
ZeroDivisionError: division by zero

后面的两行 print 永远不会执行。

三、异常处理基础:try...except(定义 + 示例)

3.1 基本语法定义

定义

  • try 块:存放可能出现问题的代码

  • except 块:当 try 中的代码出现异常时,程序自动跳转到 except 块执行。

  • 如果 try 中没有异常,except 块会被跳过。

  • 无论是否发生异常,try...except 结构后面的代码都会继续执行(除非程序被强制退出)。

代码示例

print('欢迎使用本程序')
try:
    a = int(input('请输入第一个数字:'))
    b = int(input('请输入第二个数字:'))
    res = a / b
    print(f'{a}除以{b}的结果为:{a/b}')
except:
    print('程序出现异常!!!')
print('这是后续的其他的逻辑1')
print('这是后续的其他的逻辑2')

3.2 捕获指定类型的异常(定义)

定义except 后面可以指定具体的异常类型(如 ZeroDivisionErrorValueError)。这样只有当 try 中抛出指定类型的异常时,才会进入该 except 块。这比捕获所有异常更精确、更安全。

代码示例

print('欢迎使用本程序')
try:
    a = int(input('请输入第一个数字:'))
    b = int(input('请输入第二个数字:'))
    res = a / b
    print(f'{a}除以{b}的结果为:{a/b}')
except ZeroDivisionError:
    print('除数不能为零')
except ValueError:
    print('请输入有效的整数')

3.3 验证异常之间的继承关系(定义 + 示例)

定义:Python 中所有异常都继承自 BaseException,而大多数常规异常继承自 Exception。了解继承关系有助于我们安排 except 的顺序:子类异常应写在父类异常前面,否则父类会提前捕获,导致子类异常无法被精确处理。

代码示例

# 验证异常之间的继承关系
print(ZeroDivisionError, ArithmeticError)
print(issubclass(ZeroDivisionError, ArithmeticError))  # True
print(issubclass(ZeroDivisionError, Exception))        # True
print(issubclass(ZeroDivisionError, BaseException))    # True
print(issubclass(ValueError, BaseException))           # True
print(issubclass(KeyboardInterrupt, Exception))        # False (KeyboardInterrupt 继承自 BaseException)

3.4 多个 except 的匹配顺序(定义 + 示例)

定义:当有多个 except 块时,Python 会从上往下依次匹配异常类型,一旦匹配成功,就执行对应的 except 块,并且不再继续向下匹配。因此,应该将更具体的异常(子类)放在前面,更通用的异常(父类)放在后面。

代码示例

print('欢迎使用本程序')
try:
    a = int(input('请输入第一个数字:'))
    b = int(input('请输入第二个数字:'))
    res = a / b
    print(f'{a}除以{b}的结果为:{a/b}')
except ZeroDivisionError:
    print('程序出现了异常1(除零错误)')
except ValueError:
    print('程序出现了异常2(值错误)')
except Exception:
    print('程序出现其他异常!!')   # 这个会捕获所有未在前面匹配到的异常
print('后续逻辑继续执行')

3.5 获取异常信息(定义 + 示例)

定义:使用 as e 可以将捕获到的异常对象赋值给变量 e,通过 e 可以获取异常的类型、详细信息等,便于日志记录或更精细的处理。

代码示例

print('欢迎使用本程序')
try:
    a = int(input('请输入第一个数字:'))
    b = int(input('请输入第二个数字:'))
    res = a / b
    print(f'{a}除以{b}的结果为:{a/b}')
except ZeroDivisionError:
    print('程序出现了异常1')
except ValueError:
    print('程序出现了异常2')
except Exception as e:
    print('程序出现异常!!')
    print(f'异常类型:{type(e)}')
    print(f'异常信息:{e}')
print('后续逻辑继续执行')

3.6 一个 except 捕获多个异常(定义 + 示例)

定义:可以使用元组 (异常类型1, 异常类型2, ...) 在一个 except 块中捕获多种异常。然后在块内部可以通过 isinstance() 判断具体是哪种异常,分别处理。

代码示例

print('欢迎使用本程序')
try:
    a = int(input('请输入第一个数字:'))
    b = int(input('请输入第二个数字:'))
    res = a / b
    print(f'{a}除以{b}的结果为:{a/b}')
except (ZeroDivisionError, ValueError) as e:
    print('程序出现了异常')
    print(f'异常类型:{type(e)}')
    print(f'异常信息:{e}')
    if isinstance(e, ZeroDivisionError):
        print('0不能作为除数')
    elif isinstance(e, ValueError):
        print('你必须输入整数')

四、完整异常处理结构:try-except-else-finally(定义 + 示例)

定义

  • else:只有在 try 块没有发生任何异常时才会执行。

  • finally无论是否发生异常,都会在最后执行。通常用于释放资源(如关闭文件、断开数据库连接)。

代码示例

print('欢迎使用本程序')
try:
    a = int(input('请输入第一个数字:'))
    b = int(input('请输入第二个数字:'))
    res = a / b
    print(f'{a}除以{b}的结果为:{a/b}')
except (ZeroDivisionError, ValueError) as e:
    print('程序出现了异常')
    print(f'异常信息:{e}')
else:
    print('代码没有异常,计算成功')   # 只有没有异常时才会执行
finally:
    print('无论是否有异常,我都会执行')  # 总是执行
print('后续逻辑继续执行')

五、手动抛出异常:raise(定义 + 示例)

定义:当业务逻辑不符合预期时(例如年龄不在合理范围内),可以使用 raise 语句主动抛出异常raise 后面通常跟一个异常类的实例(如 ValueError('错误信息'))。这样做可以让调用者明确知道发生了业务层面的错误。

代码示例

print('年龄判断')
try:
    age = int(input('请输入你的年龄:'))
    if 18 <= age <= 120:
        print('你成年了')
    elif 0 <= age < 18:
        print('未成年')
    else:
        # 手动抛出异常,自定义错误信息
        raise ValueError('年龄应为0-120')
except Exception as e:
    print(f'异常:{e}')

六、异常的传递机制(定义 + 示例)

定义:如果当前函数没有捕获异常,那么该异常会沿着调用链逐层向上传递,直到被某个 try-except 捕获。如果所有调用者都没有处理,最终程序会因为未处理异常而终止。这种机制称为异常的传递(或冒泡)

代码示例

def func1():
    print("func1 开始")
    return 1 / 0   # 产生 ZeroDivisionError
    print("func1 结束")   # 不会执行

def func2():
    print("func2 开始")
    func1()        # 没有处理异常,异常向上传递
    print("func2 结束")   # 不会执行

def func3():
    print("func3 开始")
    try:
        func2()
    except ZeroDivisionError as e:
        print(f"在 func3 中捕获到异常:{e}")
    print("func3 结束")

# 调用 func3
func3()
输出:

func3 开始
func2 开始
func1 开始
在 func3 中捕获到异常:division by zero
func3 结束

七、自定义异常(定义 + 示例)

定义:当内置异常无法准确表达业务需求时,可以自定义异常类。规则如下:

  1. 类名通常以 Error 结尾(推荐)。

  2. 必须继承自 Exception 或其子类。

  3. 可以重写 __init__ 方法,添加自定义的错误信息或属性。

代码示例

# 自定义异常类
class SchoolNameError(Exception):
    def __init__(self, msg):
        # 调用父类的初始化方法,传入错误信息
        super().__init__('校验异常:' + msg)

def check_name(name):
    if len(name) >= 4:
        raise SchoolNameError('学校名字过长')
    else:
        print('名字没问题')

try:
    check_name('五六七八大学')
except SchoolNameError as e:
    print(f'程序异常:{e}')
输出:

程序异常:校验异常:学校名字过长

八、总结(表格)

知识点 核心定义 关键字/结构
错误(Error) 语法错误,无法运行 无处理机制
异常(Exception) 语法正确,运行时出错 try...except
捕获指定异常 只处理特定类型的异常 except ZeroDivisionError:
获取异常信息 访问异常对象的详细信息 except ... as e:
一个except多异常 用元组同时捕获多种异常 except (Type1, Type2):
else 无异常时执行 else:
finally 无论是否异常都执行(释放资源) finally:
手动抛出异常 主动触发异常 raise
异常传递 异常沿调用链向上冒泡 自动发生
自定义异常 继承 Exception,满足业务需求 class MyError(Exception):

掌握异常处理,是写出稳定、可维护 Python 代码的关键一步。希望这篇文章能帮你彻底搞懂 Python 中的错误与异常!

📌 文中所有代码示例均已测试通过(Python 3.x),你可以直接复制运行体验效果。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐