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

在这里插入图片描述

你好,我是安然无虞。

文章目录

    • 异常处理
      • 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块用于处理异常情况.

try:# 可能引发异常的代码块
except ExceptionType:# 处理异常的代码块
  • try块中的代码会按顺序执行, 如果发生异常, 则会跳转到匹配的except块.
  • except块中的代码会处理特定类型的异常, ExceptionType表示要捕获的异常类型.
  • 可以有多个except块, 每个块可以处理不同类型的异常, 或者使用一个块来处理多个异常类型.
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)
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
捕获所有的异常信息
 # ---------方式一----------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关键字将异常信息赋给一个变量, 以便进一步处理或打印异常信息.

 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块中的代码.

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

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

 # 示例代码, 大家自己跑到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块中的代码执行完毕后无论是否发生异常都会执行.

 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块来执行无论是否发生异常都会执行的代码块, 所以经常用来处理资源回收.

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()
# 如果在函数体的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 语句, 我们可以选择性地在代码中引发特定类型的异常, 并提供相应的错误信息.这样, 我们就能够在需要的时候中断程序的正常执行流程, 并进行适当的异常处理.

语法:

 raise [ExceptionType([args])]# ExceptionType 是要引发的异常类型.# args 是一个可选参数, 用于向异常类传递附加的信息.# 如果只写一个raise, 则会默认引发RuntimeError.# raise语句后的代码不会执行.(类似return)
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的类,表示各种类型的错误.

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

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

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

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

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. 函数调用里面产生的异常

请看下面这段代码:

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('程序正常退出')

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

进入 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 中有个 列表索引越界的错误.

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

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

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

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

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

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

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

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

补充练习

案例: 模拟用户名校验.

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)
"""
对之前写的通讯录加上异常处理机制 使用异常捕获完善. 使其健壮性更强.
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.mzph.cn/bicheng/75003.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PyQt6实例_批量下载pdf工具_使用pyinstaller与installForge打包成exe文件

目录 前置&#xff1a; 步骤&#xff1a; step one 准备好已开发完毕的项目代码 step two 安装pyinstaller step three 执行pyinstaller pdfdownload.py&#xff0c;获取初始.spec文件 step four 修改.spec文件&#xff0c;将data文件夹加入到打包程序中 step five 增加…

Axure项目实战:智慧城市APP(完整交互汇总版)

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 课程主题&#xff1a;智慧城市APP 主要内容&#xff1a;主功能&#xff08;社保查询、医疗信息、公交查询等&#xff09;、活动、消息、我的页面汇总 应用场景&#xff…

Appium Inspector使用教程

1.下载最新版本 https://github.com/appium/appium-inspector/releases 2.本地启动一个Appium服务 若Android SDK已安装Appium服务&#xff0c;则在任意terminal使用appium启动服务即可 3.Appium Inspector客户端配置连接到Appium服务 Configuring and Starting a Session…

Pycharm(七):几个简单案例

一.剪刀石头布 需求&#xff1a;和电脑玩剪刀石头布游戏 考察点&#xff1a;1.随机数&#xff1b;2.判断语句 import random # numrandom.randint(1,3) # print(num) # print(**30) #1.录入玩家手势 playerint(input(请输入手势&#xff1a;&#xff08;1.剪刀 2.石头 3&…

Python Cookbook-4.13 获取字典的一个子集

任务 你有一个巨大的字典&#xff0c;字典中的一些键属于一个特定的集合&#xff0c;而你想创建一个包含这个键集合及其对应值的新字典。 解决方案 如果你不想改动原字典: def sub_dict(somedict,somekeys,default None):return dict([(k, somedict.get(k,default)) for k…

VMware Ubuntu 网络配置全攻略:从断网到畅通无阻

一、网络连接模式选择&#xff08;先搞懂原理&#xff09; VMware提供三种网络模式&#xff0c;就像手机的不同网络套餐&#xff1a; 模式适用场景特点类比NAT个人上网/新手首选虚拟机共享主机IP&#xff0c;能上网但隐身家用WiFi桥接服务器/需要被局域网访问虚拟机会获得独立…

链表(C++)

这是本人第二次学习链表&#xff0c;第一次学习链表是在大一上的C语言课上&#xff0c;首次接触&#xff0c;感到有些难&#xff1b;第二次是在大一下学习数据结构时&#xff08;就是这次&#xff09;&#xff0c;使用C再次理解链表。同时&#xff0c;这也是开启数据结构学习写…

【SPP】蓝牙串口协议应用层深度解析:从连接建立到实战开发

目录 一、SPP应用层协议框架与角色模型 1.1 分层协议栈模型 1.2 设备角色模型&#xff08;DevA 与 DevB 交互&#xff09; 二、连接建立流程&#xff1a;从 SDP 到 RFCOMM 2.1 服务发现&#xff08;SDP&#xff09;流程&#xff08;SDP 记录关键参数&#xff09; 2.2 连接…

Giteki 认证:无线产品进入日本市场的关键保障

目录 适用产品认证范围 认证项目及技术要求 认证流程 认证周期 与其他认证的对比 常见问题 注意事项 Giteki 认证&#xff0c;其名称来源于日本语 “技適マーク”&#xff0c;罗马字拼写为 “GITEKI” &#xff0c;在行业内也常被称为 Telec 认证、MIC 认证、RF 认证或技…

Ubuntu24.04 配置远程桌面服务

一&#xff1a;安装 sudo apt update sudo apt install vino 二&#xff1a;设置 gsettings set org.gnome.Vino require-encryption false # 关闭加密&#xff08;某些 VNC 客户端不支持加密&#xff09; gsettings set org.gnome.Vino prompt-enabled false # 关闭连接…

人工智能与软件工程结合的发展趋势

AI与软件工程的结合正在深刻改变软件开发的流程、工具和方法&#xff0c;其发展方向涵盖了从代码生成到系统维护的整个生命周期。以下是主要的发展方向和技术趋势&#xff1a; 1. 软件架构体系的重构 从“面向过程”到“面向目标”的架构转型&#xff1a; AI驱动软件设计以目标…

转发和重定向的区别详解

转发&#xff08;Forward&#xff09;和重定向&#xff08;Redirect&#xff09;是 Web 开发中两种常用的请求处理方式&#xff0c;主要用于将客户端请求从一个资源转移到另一个资源。它们在实现机制、行为表现和应用场景上有显著区别&#xff0c;以下是对两者的详细解析&#…

python专题1-----判断一个变量是否是字符串类型

在 Python 中&#xff0c;可以使用 isinstance() 函数来判断一个变量是否是字符串类型。字符串在 Python 中是以 str 类型表示的。下面是一些示例代码&#xff0c;展示如何判断一个变量是否是字符串类型&#xff1a; # 示例变量 var1 "Hello, World!" var2 12345 …

软件工程之需求工程(需求获取、分析、验证)

一、需求获取&#xff08;Requirements Elicitation&#xff09; 1. 定义与目标 需求获取是通过与用户、利益相关者等交互&#xff0c;识别并捕获系统需求的过程&#xff0c;目标是明确用户意图与业务目标&#xff0c;避免后期因需求偏差导致返工。 2. 主要方法 问卷法&…

Java简单生成pdf

生成这样的PDF 直接上代码 public static void main(String[] args) {String logoPath "Q:\\IdeaWork\\Demo\\src\\main\\webapp\\images\\logo.jpg"; // 替换为实际路径String baseDir "E:/Demo/TEST/problem/Generate"; // 基础目录int year 2025; //…

k8s存储介绍(六)StorangeClass

一、Kubernetes 存储类&#xff08;StorageClass&#xff09;详解 1. 什么是 StorageClass&#xff1f; 在 Kubernetes 中&#xff0c;StorageClass&#xff08;存储类&#xff09;是一种用于动态创建 PersistentVolume&#xff08;PV&#xff09;的资源对象。它允许管理员根…

C++:allocator类(动态数组续)

1.为什么需要 allocator&#xff1f; 在 C 中&#xff0c;动态内存管理通常通过 new 和 delete 完成&#xff1a; int* p new int; // 分配内存 构造对象 delete p; // 析构对象 释放内存 但 new 和 delete 有两个问题&#xff1a; 耦合性&#xff1a;将内…

北斗导航 | 中国北斗卫星导航系统的发展历程——“三步走”战略:背景,信号频点,调制方式,短报文,等

中国北斗卫星导航系统的发展历程按照“三步走”战略逐步推进,从区域服务到全球覆盖,形成了北斗一号、北斗二号、北斗三号三代系统的迭代升级,展现了中国航天科技的自主创新与突破。以下是各阶段的核心内容与发展特点综述:一、北斗一号:中国卫星导航的奠基(1994-2003年) …

Headless Chrome 优化:减少内存占用与提速技巧

在当今数据驱动的时代&#xff0c;爬虫技术在各行各业扮演着重要角色。传统的爬虫方法往往因为界面渲染和资源消耗过高而无法满足大规模数据采集的需求。本文将深度剖析 Headless Chrome 的优化方案&#xff0c;重点探讨如何利用代理 IP、Cookie 和 User-Agent 设置实现内存占用…

英伟达GB300新宠:新型LPDDR5X SOCAMM内存

随着人工智能&#xff08;AI&#xff09;、机器学习&#xff08;ML&#xff09;和高性能计算&#xff08;HPC&#xff09;应用的快速发展&#xff0c;对于高效能、大容量且低延迟内存的需求日益增长。NVIDIA在其GB系列GPU中引入了不同的内存模块设计&#xff0c;以满足这些严格…