python学习-程序异常处理

python学习-程序异常处理

本文介绍 python 如何进行程序异常处理,以及程序异常处理的各种细节:常见的异常处理、设计多组异常处理程序、丢出异常、finally 语句、程序断言 assert 和日志 logging 模块。

供自己以后查漏补缺,也欢迎同道朋友交流学习。

引言

之前的几篇文章都写的是程序怎么写的正确,但很多时候,程序运行时会报错,这时候我们就需要对程序进行异常处理,让程序可以正常运行。

所以,本篇文章主要介绍如何进行 python 的程序异常处理,以及程序异常处理的各种细节:常见的异常处理、设计多组异常处理程序、丢出异常、finally 语句、程序断言 assert 和日志 logging 模块。

程序异常

异常处理

在编程中,异常是指程序在运行时发生的错误,这些错误可能是由于代码中存在的逻辑错误、外部环境的变化(如文件不存在网络连接失败等),或者是用户的非法操作引起的。

Python 中,异常是 Exception 类或者其子类的实例。当程序执行到可能会发生错误的代码时,如果发生了异常,程序会停止执行并抛出一个异常对象。

异常处理是程序设计中的一个重要部分,它允许程序在遇到错误时不会立即崩溃,而是能够以一种控制的方式响应这些错误。

异常处理机制

Python 提供了一套完整的异常处理机制,使得程序员可以捕获和处理异常,防止程序因未处理的错误而中断执行。

  • try关键字try 块是异常处理的核心,它允许程序员测试可能引发异常的代码。如果 try 块中的代码执行成功,那么except 块将被跳过;如果发生异常,则跳转到 except 块执行。
  • except关键字except 块用于捕获并处理特定类型的异常。可以有多个 except 块,分别处理不同类型的异常。
  • else关键字else 块是可选的,仅当 try 块没有发生任何异常时执行。
  • finally关键字finally 块也是可选的,无论是否发生异常,它都会被执行。这通常用于执行清理操作,如关闭文件释放资源
python 复制代码
try:
  # 尝试执行的代码
  result = 10 / 0
except ZeroDivisionError:
  # 处理除以零的异常
  print("除以零错误")
except Exception as e:
  # 处理其他类型的异常
  print(f"发生了异常:{e}")
else:
  # 如果没有异常发生,执行这里的代码
  print("一切正常")
finally:
  # 无论是否发生异常,都会执行这里的代码
  print("清理资源")

常见的异常类型

在程序中有太多不可预期的异常发生了,所以我们需要对这些异常进行处理,让程序可以正常运行。我们有必要简单了解下有哪些常见的异常类型:

异常对象名称 说明
AttributeError 访问对象属性时发生错误
Exception 通用异常类
FileNotFoundError 文件未找到错误
IOError 输入/输出错误
IndexError 索引超出范围错误
KeyError 字典中不存在的键错误
NameError 未定义名称错误
SyntaxError 语法错误
TypeError 类型错误
ValueError 值错误
ZeroDivisionError 除以零错误
MemoryError 内存错误
SystemError 系统错误

设计多组异常处理程序

单一异常处理

单一异常处理是指针对特定类型的异常编写处理代码。这种方式允许程序对不同类型的错误做出不同的响应。

python 复制代码
value = '2.1'

# 单一异常处理
try:
  # 尝试将输入转换为整数
  number = int(value)
except ValueError:
  # 处理非整数输入的情况
  print("错误:您输入的不是一个有效的整数。")

多异常处理

多异常处理允许程序同时处理多个不同的异常。这可以通过多个 except 子句实现。

python 复制代码
value2 = '0'
try:
  # 尝试将输入转换为整数
  number2 = int(value2)
  # 尝试将整数除以零
  result = 10 / number2
except ValueError:
  # 处理非整数输入的情况
  print("错误:您输入的不是一个有效的整数。")
except ZeroDivisionError:
  # 处理除以零的情况
  print("错误:不能除以零。")

异常链

异常链是指在捕获一个异常的同时,保留原始异常的信息。这可以通过在 except 子句中使用 from 关键字来实现。

python 复制代码
value3 = '5.5'
try:
  # 尝试将输入转换为整数
  number3 = int(value)
except ValueError as e:
  # 处理非整数输入的情况,并抛出新的异常
  raise ValueError("输入错误:请输入一个有效的整数") from e

使用异常链的好处是,它允许在上层函数捕获并处理异常,同时保留原始异常的堆栈跟踪信息,这对于调试和错误日志记录非常有用。

丢出异常

手动抛出异常

手动抛出异常通常用于强制执行某些条件,或者当检测到错误状态时需要立即停止程序的进一步执行。

Python 中,可以使用 raise 关键字来手动抛出一个异常。

python 复制代码
def check_age(age):
  if age < 0:
    # 手动抛出异常
    raise ValueError("年龄不能为负数")
  else:
    print("年龄检查通过")

try:
  check_age(-5)
except ValueError as e:
  print(f"捕获到异常:{e}")

自定义异常

自定义异常允许你创建特定于你应用程序的异常类型,这可以使得错误处理更加清晰和具体。

python 复制代码
# 自定义异常
class MyException(Exception):
  """自定义异常类"""
  pass

def divide(x, y):
  if y == 0:
    raise MyException("除数不能为零")
  else:
    return x / y
  
try:
  result = divide(10, 0)
except MyException as e:
  print(f"捕获到自定义异常:{e}")

异常的传递

异常的传递涉及到异常如何在函数调用栈中从被触发的地方传递到被捕获的地方。

如果一个异常在当前函数中没有被捕获,它将被传递到调用栈中的上一个函数,依此类推,直到被捕获或者导致程序终止。

python 复制代码
def outer_function():
  try:
    inner_function()
  except ValueError as e:
    print("在外部函数中捕获异常:", e)

def inner_function():
  try:
    raise ValueError("这是一个错误")
  except ValueError as e:
    raise  # 重新抛出当前异常,允许外部函数捕获

outer_function()

finally 语句

finally的作用

finally 的主要目的是提供一个清理的环节,确保即使在发生异常时也能执行某些代码。这对于防止资源泄露和其他清理工作至关重要。

python 复制代码
try:
  f = open("file.txt", "r")
  data = f.read()
except IOError as e:
  print(f"文件操作出错:{e}")
finally:
  f.close()  # 确保文件被关闭

程序断言 assert

断言(assert)是一种用于调试的语句,它用于验证程序中的某个条件是否为真。

如果条件为真,则程序继续执行;

如果条件为假,则程序抛出 AssertionError 异常。

断言通常用于检查程序中不应该发生的情况,或者确保函数的输入参数满足预期的条件

基础用法

python 复制代码
assert 条件, 错误消息

断言的使用

python 复制代码
def calc_discount(price, discount):
  assert price > 0, "价格必须大于0"
  assert 0 <= discount <= 1, "折扣必须在0到1之间"
  return price * (1 - discount)

try:
  print(calc_discount(100, -0.2))
except AssertionError as e:
  print(f"断言错误: {e}")

日志模块 logging

日志模块概述

logging 模块允许程序生成日志信息,这些信息可以输出到不同的地方,如控制台文件网络等。

提供了日志记录的四个主要组成部分:日志记录器(Loggers)、日志处理器(Handlers)、日志格式化器(Formatters)和日志过滤器(Filters)。

可以通过 logging.basicConfig() 函数快速配置日志系统,设置日志级别日志文件名日志格式等。

日志级别

logging 模块定义了五个日志级别,按严重性从低到高排序如下:

  • DEBUG :详细的信息,通常只在诊断问题时有用。
  • INFO :确认程序按预期运行的信息
  • WARNING :指示有潜在问题的情况(例如,程序回退到一个更慢的算法)。
  • ERROR :由于更严重的问题,程序的某些功能无法执行
  • CRITICAL严重错误,表明程序本身可能无法继续运行。
python 复制代码
import logging

logging.basicConfig(level=logging.DEBUG)
logging.debug("这是一条 debug 级别的日志")
logging.info("这是一条 info 级别的日志")
logging.warning("这是一条 warning 级别的日志")
logging.error("这是一条 error 级别的日志")
logging.critical("这是一条 critical 级别的日志")

自定义日志格式和处理器

日志格式

日志的格式可以通过创建 Formatter 对象来自定义。

python 复制代码
import logging

format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
logging.basicConfig(level=logging.DEBUG, format=format)
logger = logging.getLogger(__name__)
logger.debug('这是一条 debug 级别的日志')

自定义日志处理器

日志处理器决定了日志信息的输出位置,如控制台、文件等。

python 复制代码
import logging

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

file_handler = logging.FileHandler('app.log')
file_handler.setFormatter(formatter)

logger = logging.getLogger(__name__)
logger.addHandler(file_handler)
logger.setLevel(logging.DEBUG)
logger.info("这条日志将被写入文件")

python学习专栏系列

练习代码库地址

python-study

相关推荐
.生产的驴15 分钟前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
我曾经是个程序员17 分钟前
鸿蒙学习记录
开发语言·前端·javascript
顽疲21 分钟前
springboot vue 会员收银系统 含源码 开发流程
vue.js·spring boot·后端
羊小猪~~32 分钟前
前端入门之VUE--ajax、vuex、router,最后的前端总结
前端·javascript·css·vue.js·vscode·ajax·html5
摸鱼了1 小时前
🚀 从零开始搭建 Vue 3+Vite+TypeScript+Pinia+Vue Router+SCSS+StyleLint+CommitLint+...项目
前端·vue.js
程序员shen1616111 小时前
抖音短视频saas矩阵源码系统开发所需掌握的技术
java·前端·数据库·python·算法
人人人人一样一样1 小时前
作业Python
python
Ling_suu1 小时前
SpringBoot3——Web开发
java·服务器·前端
Yvemil72 小时前
《开启微服务之旅:Spring Boot Web开发》(二)
前端·spring boot·微服务