31天Python入门——第14天:异常处理

news/2025/4/2 5:42:05/

在这里插入图片描述

你好,我是安然无虞。

文章目录

    • 异常处理
      • 1. Python异常
      • 2. 异常捕获
        • try-except语句
        • 捕获所有的异常信息
        • 获取异常对象
        • finally块
      • 3. raise语句
      • 4. 自定义异常
      • 5. 函数调用里面产生的异常
      • 补充练习

在这里插入图片描述

异常处理

1. Python异常

Python异常指的是在程序执行过程中发生的错误或异常情况. 当代码遇到错误时, 会引发异常并中断程序的正常执行流程.

异常提供了一种机制来处理错误, 以便程序可以在错误发生时采取适当的行动, 而不会导致程序崩溃或产生意外结果.

在Python中, 异常是通过异常类表示的. 每个异常类都是Python内置的或自定义的, 用于表示特定类型的错误. 常见的内置异常类包括:

  • SyntaxError:语法错误, 通常是代码书写不正确.
  • NameError:名称错误, 尝试访问不存在的变量或函数.
  • TypeError:类型错误, 操作或函数应用于不兼容的数据类型.
  • ValueError:值错误, 当函数接收到不合法的值时引发.
  • ZeroDivisionError:零除错误, 尝试将一个数除以零.
  • Exception:所有内置异常类的基类, 即其他所有的异常都是基于它的.

除了这些内置异常类, Python还提供了许多其他的异常类, 用于特定的错误情况. 可以使用try-except语句来捕获和处理异常. 通过在try块中编写可能引发异常的代码, 并在except块中处理异常, 可以保护程序免受错误的影响(在程序出现异常时不会直接中断程序执行), 而是采取适当的措施来处理异常情况.

异常处理可以帮助我们提高程序的健壮性和容错能力, 使得程序能够更好地处理异常情况, 并给出恰当的反馈或采取相应的措施.

2. 异常捕获

try-except语句

在Python中, 我们使用try-except语句来捕获和处理异常. try块用于编写可能引发异常的代码, 而except块用于处理异常情况.

python">try:# 可能引发异常的代码块
except ExceptionType:# 处理异常的代码块
  • try块中的代码会按顺序执行, 如果发生异常, 则会跳转到匹配的except块.
  • except块中的代码会处理特定类型的异常, ExceptionType表示要捕获的异常类型.
  • 可以有多个except块, 每个块可以处理不同类型的异常, 或者使用一个块来处理多个异常类型.
python">def divide_numbers(a, b):result = a / bprint("result", result)divide_numbers(10, 2) # 5.0
divide_numbers(10, 0) # 触发除零异常, 程序中断, 后续代码都不会被执行 - 这样的话代码的容错能力很低
divide_numbers(9, 2)
divide_numbers(8, 2)
python">def divide_numbers(a, b):try:result = a / bprint("result", result)except ZeroDivisionError:print("0不能作为除数.")except TypeError:print("参数传递类型错误.")except Exception as e:print(f"程序出现了意外错误: 具体出错内容: {e}.")finally:print(f"{a}/{b}的结果计算完毕.")divide_numbers(10, 2) # 5.0
divide_numbers(10, 0) # 显示‘0不能作为除数’
divide_numbers(9, 2) # 4.5
divide_numbers(8, 2) # 4.0
捕获所有的异常信息
python"> # ---------方式一----------try:# 可能引发异常的代码块except:# 处理所有异常def divide_numbers(a, b):try:result = a / bexcept:print("程序出错了.")# ---------方式二----------# 根据基类Exception来捕获def divide_numbers(a, b):try:result = a / bexcept Exception:print("程序出错了.")
获取异常对象

在异常处理块中, 可以使用as关键字将异常信息赋给一个变量, 以便进一步处理或打印异常信息.

python"> try:# 可能引发异常的代码块except ExceptionType as e:# 处理异常, 并使用变量e访问异常信息def divide_numbers(a, b):try:result = a / bexcept Exception as e: # 注意: 根据基类Exception来捕获异常只能放在最后不能放在其他异常类之前print(f"程序出现了意外错误: 具体的出错内容: {e}")

else块

else块是try-except语句的可选部分, 用于指定在try块中的代码执行完毕且没有引发异常时要执行的代码. 如果在try块中发生了异常并被捕获, 则不会执行else块中的代码.

python"> try:# 可能引发异常的代码块except ExceptionType:# 处理异常的代码块else:# 在没有发生异常时执行的代码块, 发生异常时就不会执行

注意点: 在try里如果return了, 即使没有报错, 也不会执行else里的代码

python"> # 示例代码, 大家自己跑到pycharm里运行的试试.def test():try:# 执行一些操作result = 3return result  # 在这里返回结果,函数立即退出except ValueError:# 处理异常passelse:# 这里的代码不会执行print("This code will not be executed.")# 调用函数result = test()print(result)

使用 else 块可以提高代码的可读性和可维护性, 但并不是所有人都认同这种做法.有些开发者更倾向于只使用 try-except 块, 将异常处理的逻辑放在一起, 使代码更加紧凑.总之, 使用 try-else 结构是一种代码组织的选择, 而不是强制性的规范.在实际开发中, 我们应根据代码的需求、可读性和一致性等因素来决定是否使用 else

很多大牛在开发中几乎不使用 else, 因为对代码的可读性并没有多大的提升.

所以了解即可.

finally块

除了try-except语句, 还可以使用finally(可选的)块来执行无论是否发生异常都需要执行的代码. finally块中的代码在try块中的代码执行完毕后无论是否发生异常都会执行.

python"> try:# 可能引发异常的代码块except ExceptionType:# 处理异常的代码块finally:# 无论是否发生异常, 都会执行的代码块def divide_numbers(a, b):try:result = a / bprint("result", result)except ZeroDivisionError:print("0不能作为除数.")except TypeError:print("参数传递类型错误.")except Exception as e:print(f"程序出现了意外错误: 具体出错内容: {e}.")finally:print(f"{a}/{b}的结果计算完毕.")

由于finally块来执行无论是否发生异常都会执行的代码块, 所以经常用来处理资源回收.

python">try:# 没有使用with语句打开文件的话, 需要手动关闭文件f = open('./contact1.txt', 'r')f.close()
except FileNotFoundError:print("不存在此文件")# 上面的代码是有问题的
# 如果打开文件的时候出错了, 比如当前目录下没有这个文件就会导致异常
# 打开文件导致异常后就不会执行f.close()关闭文件
# 但是有一点还需要格外注意就是如果是由于没有找到这个文件触发异常就会在打开文件的时候触发异常然后直接跳到except FileNotFoundError执行后面的代码, 由于表达式的执行是先计算右边的值然后再将计算结果赋值给左边, 这里由于打开文件触发异常导致没有执行赋值语句, 所以此时f变量是不存在的, 后续调用f.close()是没有意义的.# 还有一种情况是, 如果打开文件没有问题, 但是在后续处理异常时出现问题
# 这个时候就可以使用finally来关闭文件
try:f = open('./contact.txt', 'r')content = f.read()# 后续处理# ...
except FileNotFoundError:print("不存在此文件")
except Exception:print("后续处理异常")
finally:f.close()
python"># 如果在函数体的try代码块中有return语句和finally, 它们的执行顺序是怎样的:
def divide_numbers(a, b):try:result = a / breturn resultexcept ZeroDivisionError:print("0不能作为除数.")except TypeError:print("参数传递类型错误.")except Exception as e:print(f"程序出现了意外错误: 具体出错内容: {e}.")finally:print(f"{a}/{b}的结果计算完毕.")res1 = divide_numbers(10, 2)# 执行结果
# 10/2的结果计算完毕.
# 5.0# 从上面的执行结果说明了:
# 即使在函数定义里的try代码块中使用了return语句还是会执行后面finally代码块中的内容

总结

异常通常用于处理意外或异常的情况, 即那些无法在代码中预测和处理的情况.
当你无法准确预测可能出现的错误或无法在当前上下文中处理错误时, 抛出异常是一种更合适的方式.
异常提供了一种机制, 让你能够在错误发生时终止当前的代码执行, 并将控制权交给上层代码或异常处理机制

3. raise语句

raise语句是 Python 中的一个关键字, 用于手动引发异常.(前面都是自动触发异常)
通过 raise 语句, 我们可以选择性地在代码中引发特定类型的异常, 并提供相应的错误信息.这样, 我们就能够在需要的时候中断程序的正常执行流程, 并进行适当的异常处理.

语法:

python"> raise [ExceptionType([args])]# ExceptionType 是要引发的异常类型.# args 是一个可选参数, 用于向异常类传递附加的信息.# 如果只写一个raise, 则会默认引发RuntimeError.# raise语句后的代码不会执行.(类似return)
python">def check_age(age):if age < 18:raise ValueError('年龄小于18岁, 不允许注册.') # 手动抛出异常else:# 注册代码如下:passprint('恭喜, 注册成功')try:age = int(input("请输入年龄:"))check_age(age)
except ValueError as e:print(e) # 可以把在上面的raise中指定的抛出异常的信息打印出来, 请细品raise的使用 - 可以Chat一下# 执行结果:
# 请输入年龄:17
# 年龄小于18岁, 不允许注册.

使用注意事项:

  1. 选择适当的异常类型:根据具体的情况, 选择合适的异常类型来反映错误的性质. Python 提供了许多内置的异常类, 如 ValueErrorTypeErrorFileNotFoundError 等, 可以根据需要选择合适的异常类或自定义异常类.
  2. 提供明确的错误信息:在引发异常时, 尽量提供清晰、明确的错误信息, 以便在程序出错时能够准确地定位和理解错误的原因.
  3. 在适当的位置引发异常:raise语句应该放置在程序逻辑中合适的位置, 以便在需要时引发异常.根据代码的要求, 可以在函数、方法或其他控制流结构中使用 raise 语句.
  4. 捕获和处理异常:使用 try-except 块捕获和处理引发的异常.根据具体的异常类型, 编写相应的错误处理代码, 以便优雅地处理异常情况.
  5. 避免滥用 raise 语句:raise 语句应该用于合适的情况, 不应该滥用.只有在必要的时候才使用 raise语句, 以避免引发不必要的异常.

4. 自定义异常

异常类型都是 继承自Exception的类,表示各种类型的错误.

我们也可以自己定义异常,比如我们写一个用户注册的函数, 要求用户输入的电话号码只能是中国的电话号码,并且电话号码中不能有非数字字符.

可以定义下面这两种异常类型:

python"># 异常对象,代表电话号码有非法字符
class InvalidCharError(Exception):pass# 异常对象,代表电话号码非中国号码
class NotChinaTelError(Exception):pass

定义了上面的异常,当用户输入电话号码时,出现相应错误的时候,我们就可以使用raise 关键字来抛出对应的自定义异常.

python">def  register():tel = input('请注册您的电话号码:')# 如果有非数字字符if not tel.isdigit(): raise InvalidCharError()# 如果不是以86开头,则不是中国号码if not tel.startswith('86'): raise NotChinaTelError()return teltry:ret = register()
except InvalidCharError:print('电话号码中有错误的字符')
except NotChinaTelError:print('非中国手机号码')

5. 函数调用里面产生的异常

请看下面这段代码:

python">def level_3():print ('进入 level_3')a = [0]b = a[1]print ('离开 level_3')def level_2():print ('进入 level_2')level_3()print ('离开 level_2')def level_1():print ('进入 level_1')level_2()print ('离开 level_1')level_1()print('程序正常退出')

运行该代码会得到类似下面的结果:

python">进入 level_1
进入 level_2
进入 level_3
Traceback (most recent call last):File "E:\err.py", line 18, in <module>level_1()File "E:\err.py", line 14, in level_1level_2()File "E:\err.py", line 9, in level_2level_3()File "E:\err.py", line 4, in level_3b = a[1]
IndexError: list index out of range

函数调用次序是这样的

主体部分调用 函数 level_1

函数level_1调用 函数level_2

函数level_2调用 函数level_3

大家注意:函数 level_3 中有个 列表索引越界的错误.

所以执行到该函数的时候,解释器报错了。它在终端上显示了错误代码的具体位置. 也就是:

python">File "E:\err.py", line 4, in level_3b = a[1]

大家可以发现,上面还有输出的信息,说明了这行引起异常的代码, 是怎样被 一层层 的调用进来的.

这就是函数调用栈的信息.

当异常在函数中产生的时候,解释器会终止当前代码的执行, 查看当前函数是否 声明了该类型异常的 except 处理,如果有,就执行, 随后继续执行代码.

如果当前函数没有 声明了该类型异常的处理, 就会中止当前函数的执行,退出到调用该函数的上层函数中, 查看上层是否有 声明了该类型异常的 except 处理. 如果有,就执行该异常匹配处理. 随后继续执行代码.

如果上层函数也没有 该类型异常的匹配处理, 就会到继续到再上层的函数查看是否有 该类型异常的匹配处理.

如此这般,直到到了最外层的代码. 如果依然没有 声明了该类型异常处理,就终止当前代码的执行.

补充练习

案例: 模拟用户名校验.

python">def check_username(username):"""1. 长度不能小于5.2. 只能包含字符.3. 禁止使用系统用户名. admin, root.:param username: 传入的用户名.:return: None."""if username in ['admin', 'root']:raise ValueError('禁止使用系统用户名')if len(username) < 5:raise ValueError('用户名长度小于5')if not username.isalpha():raise ValueError('用户名只能包含字符')# 如果上面的3个判断都没进, 就会走到这里.print(f'{username}校验成功.')try:username = input("请输入用户名:")check_username(username)
except ValueError as e:print(e)
python">"""
对之前写的通讯录加上异常处理机制 使用异常捕获完善. 使其健壮性更强.
1. 如果用户输入了我们规则之外的指令. '1 2 3 4 5'以外的. 应该如何处理.
2. 读写我们上节课使用的'r+', 实际上读跟写要分离开, 才是最好的.
3. 若是我们本地没有通讯录文件的时候, 第一次读取, 会出错, 如何处理.
4. 删除联系人时, 如果用户输入错了想要删除的人, 如想要删除'张', 输入成了'刘', 容易出现误删, 而且会出现删除通讯录里不存在的人. 应该如何处理.
5. 手机号是不存在字母的, 如果用户输入有字母的话, 应该如何处理.
"""
import jsondef read_file():try:with open("./contact.txt", 'r', encoding='utf8') as f:content = f.read()return contentexcept FileNotFoundError:return Falsedef write_file(content):with open("./contact.txt", 'w', encoding='utf8') as f:f.write(content)def query_all():content = read_file()if content:# 如果存在, 就要展示所有的联系人.try:json_data = json.loads(content)if not json_data:print(f'该通讯录目前没有联系人, 请先添加联系人')returnexcept json.decoder.JSONDecodeError:print('系统出错, 稍后在尝试.')# 给开发人员发出提醒. 赶紧去修复.returnelse:print('为您查询到的所有联系人如下:')for key, val in json_data.items():print(f'{key}: {val}')else:print(f'该通讯录目前没有联系人, 请先添加联系人')def query_contact():name = input("请输入想要查询的联系人姓名:")content = read_file()if content:# 如果存在, 就要做查询操作.json_data = json.loads(content)phone = json_data.get(name)print(f'为您查询到的指定联系人{name}的电话是: {phone}')else:print(f'未找到指定联系人: {name}')def del_contact():name = input("请输入想要删除的联系人姓名:")content = read_file()if content:# 如果存在, 就要做删除操作.res = input(f"是否确认删除联系人; {name}. y/n?")if res.lower() == 'y':json_data = json.loads(content)# pop_value = json_data.pop(name, None)try:json_data.pop(name)except KeyError:print('没有找到指定的联系人.')returnwrite_file(json.dumps(json_data, ensure_ascii=False))print(f'删除{name}成功')else:print('已取消删除操作')else:# 如果不存在, 就没办法删了.print(f'未找到指定联系人: {name}')def add_contact():name = input("请输入联系人姓名:")phone = input("请输入联系人电话:")try:int(phone)except ValueError:print('您输入的手机号并不是纯数字的. 请重新添加')returncontact_dict = {name: phone}content = read_file()if content:json_data = json.loads(content)json_data.update(contact_dict)else:json_data = contact_dictwrite_file(json.dumps(json_data, ensure_ascii=False))print(f'添加{name}成功')def menu():print("欢迎使用通讯录管理系统")print("------welcome-----")print("菜单选项:")print("1. 添加联系人.")print("2. 删除联系人.")print("3. 查询指定联系人.")print("4. 查看所有的联系人.")print("5. 退出通讯录.")while True:try:choice = int(input("请输入您想要操作的选项:"))except ValueError:print('请输入1-2-3-4-5的对应指令.')continueif choice == 1:add_contact()elif choice == 2:del_contact()elif choice == 3:query_contact()elif choice == 4:query_all()elif choice == 5:print('已退出通讯录管理系统')breakelse:print('请输入1-2-3-4-5的对应指令.')menu()
遇见安然遇见你,不负代码不负卿。
谢谢老铁的时间,咱们下篇再见~

http://www.ppmy.cn/news/1584534.html

相关文章

AWS Bedrock 多代理蓝图存储库使用 CDK、Streamlit 和 LangFuse 运行 AWS Bedrock 多代理 AI 协作的蓝图

一、软件下载 文末提供程序和源码下载 AWS Bedrock 多代理蓝图开源程序&#xff01;是一个全面基础设施即代码 &#xff08;IaC&#xff09; 解决方案&#xff0c;用于使用 CDK、Streamlit 和 LangFuse 在 AWS 上运行多代理 AI 协作。无论您是经验丰富的开发人员还是刚刚开始使…

Cursor 使用 APIkey 配置 Anthropic Claude BaseURL , gpt-4o,deepseek等大模型代理指南

背景 Cursor IDE 原生仅支持配置 ChatGPT 的 API Base URL&#xff0c;无法直接集成 ​Anthropic Claude 的 API 服务。 解决方案 本文将通过以下方式实现 Cursor 集成 Claude API&#xff1a; ✅ 构建中转 API 桥接层 ✅ 配置自定义 API 端点 ✅ 实现协议格式转换 前置条件…

瑞芯微RKRGA(librga)Buffer API 分析

一、Buffer API 简介 在瑞芯微官方的 librga 库的手册中&#xff0c;有两组配置 buffer 的API&#xff1a; importbuffer 方式&#xff1a; importbuffer_virtualaddr importbuffer_physicaladdr importbuffer_fd wrapbuffer 方式&#xff1a; wrapbuffer_virtualaddr wrapb…

flutter优秀项目推荐

以下是几个值得推荐的 Flutter 开源项目&#xff0c;涵盖不同领域&#xff08;UI 库、工具、完整应用等&#xff09;&#xff0c;适合学习和实际开发参考&#xff1a; 1. UI 组件库 & 动画 (1) flutter_ui_challenges 亮点&#xff1a;100 个精美的 UI 设计实现&#xff…

通过 Adobe Acrobat DC 实现 Word 到 PDF 的不可逆转换

目录 一、前言二、什么是不可逆PDF转换&#xff1f;三、准备工作四、详细转换步骤4.1 基础转换4.2 文档转曲&#xff08;最终不可逆处理&#xff09;4.2.1 检查并安装所需字体&#xff08;转曲前建议完成&#xff09;4.2.2 PDF文件转曲步骤 五、验证转换效果六、常见问题解决方…

C++学习之Linux文件编译、调试及库制作

目录 1.rwx对于文件和目录的区别 2.gcc编译过程 3.数据段合并和地址回填说明 4.gcc编译其他参数 5.函数库简介 6.静态库的使用 7.动态库的简介 8.动态库制作基本流程 9.启动APP错误解决方案12 10.启动APP错误解决方案34 11.makefile一组规则 12.makefile的两个函数 …

若依专题——基础应用篇

若依搭建 搭建后端项目 ① Git 克隆并初始化项目 ② MySQL 导入与配置 ③ 启动 Redis 搭建后端项目注意事项&#xff1f; ① 项目初始化慢&#xff0c;执行clean、package ② MySQL导入后&#xff0c;修改application-druid.yml ③ Redis有密码&#xff0c;修改ap…

聚类注意点

聚类注意点 样本异常数据 K均值&#xff08;K-Means&#xff09;是聚类中最常用的方法之一&#xff0c;它基于点与点距离的相似度来计算最佳类别归属。但K均值在应用之前一定要注意两种数据异常&#xff1a; 数据的异常值&#xff1a;数据中的异常值能明显改变不同点之间的距离…