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

相关推荐
小小小小宇2 分钟前
PC和WebView白屏检测
前端
天天扭码14 分钟前
ES6 Symbol 超详细教程:为什么它是避免对象属性冲突的终极方案?
前端·javascript·面试
小矮马17 分钟前
React-组件和props
前端·javascript·react.js
Huanzhi_Lin19 分钟前
python源码打包为可执行的exe文件
python
懒羊羊我小弟21 分钟前
React Router v7 从入门到精通指南
前端·react.js·前端框架
电商api接口开发32 分钟前
ASP.NET MVC 入门指南三
后端·asp.net·mvc
声声codeGrandMaster32 分钟前
django之账号管理功能
数据库·后端·python·django
DC...1 小时前
vue滑块组件设计与实现
前端·javascript·vue.js
娃娃略1 小时前
【AI模型学习】双流网络——更强大的网络设计
网络·人工智能·pytorch·python·神经网络·学习
我的golang之路果然有问题1 小时前
案例速成GO+redis 个人笔记
经验分享·redis·笔记·后端·学习·golang·go