掌握 Python 异常处理的实战技巧:从基础到高级应用20240918
掌握 Python 异常处理的实战技巧:从基础到高级应用
引言
在 Python 编程中,异常处理是保障代码稳健性和可靠性的关键要素之一。无论是在网络请求、资源访问,还是复杂的业务逻辑中,异常处理都不可或缺。本文将从 Python 异常的基础知识入手,深入探讨如何在实战中有效地捕获和处理异常,并探讨自定义异常的重要性和实现方法。通过最佳实践的示例,帮助您提升异常处理的技能,编写出更加健壮的代码。
Python 异常的层次结构
Python 中的异常都是继承自 BaseException 类的。BaseException 是所有异常的基类,常见的异常类型分支如下:
BaseException
├── SystemExit
├── KeyboardInterrupt
├── GeneratorExit
└── Exception├── ArithmeticError├── LookupError├── ValueError├── TypeError├── ...(其他内建异常)└── ...(用户自定义异常)
BaseException:所有异常的根类,不应直接继承自此类。SystemExit、KeyboardInterrupt、GeneratorExit:这些是系统级的异常,一般不需要用户捕获。Exception:常用的异常基类,大多数自定义异常都应该继承自它。
Python 异常处理的格式
Python 提供了一个结构化的方式来处理异常,基本格式如下:
try:# 可能会抛出异常的代码
except (Exception1, Exception2) as e:# 捕获指定的异常并处理
else:# 如果没有发生异常,执行这部分代码
finally:# 无论是否发生异常,都会执行的代码(通常用于资源释放)
各部分详解
try块:包含可能引发异常的代码。如果没有异常发生,程序会跳过except块,执行else块(如果存在)。except块:用于捕获并处理指定的异常类型。可以有多个except块,分别处理不同类型的异常。else块:仅当try块中没有发生任何异常时执行。这部分代码用于在成功执行try块后,需要额外执行的操作。finally块:无论是否发生异常,都会执行的代码。通常用于释放资源,如关闭文件、网络连接等。
注意事项
- 未被捕获的异常不会进入
else块:如果在try块中发生了异常,且该异常未被任何except块捕获,else块将被跳过,异常会继续向上传播。 finally块总是会执行:无论是否发生异常,finally块的代码都会被执行,适用于资源的清理和释放。
示例:详细理解异常处理格式
def divide(a, b):try:print("进入 try 块")result = a / bexcept ZeroDivisionError as e:print(f"捕获到 ZeroDivisionError:{e}")except TypeError as e:print(f"捕获到 TypeError:{e}")else:print("没有发生异常,执行 else 块")print(f"计算结果是:{result}")finally:print("执行 finally 块,无论是否发生异常")# 示例调用
print("测试 a=10, b=2:")
divide(10, 2)print("\n测试 a=10, b=0:")
divide(10, 0)print("\n测试 a=10, b='a':")
divide(10, 'a')
输出:
测试 a=10, b=2:
进入 try 块
没有发生异常,执行 else 块
计算结果是:5.0
执行 finally 块,无论是否发生异常测试 a=10, b=0:
进入 try 块
捕获到 ZeroDivisionError:division by zero
执行 finally 块,无论是否发生异常测试 a=10, b='a':
进入 try 块
捕获到 TypeError:unsupported operand type(s) for /: 'int' and 'str'
执行 finally 块,无论是否发生异常
解析:
-
第一次调用
divide(10, 2):try块中没有发生异常。- 跳过所有的
except块。 - 执行
else块,输出计算结果。 - 执行
finally块。
-
第二次调用
divide(10, 0):try块中发生ZeroDivisionError。- 进入对应的
except ZeroDivisionError块,处理异常。 - 跳过
else块。 - 执行
finally块。
-
第三次调用
divide(10, 'a'):try块中发生TypeError。- 进入对应的
except TypeError块,处理异常。 - 跳过
else块。 - 执行
finally块。
关于未被捕获的异常
如果在 try 块中发生了未被 except 块捕获的异常,那么:
else块将被跳过。finally块仍然会执行。- 异常将继续向上传播,直到被捕获或导致程序崩溃。
示例:未捕获的异常
def test_unhandled_exception():try:print("进入 try 块")result = undefined_variable # 未定义的变量,触发 NameErrorexcept ZeroDivisionError as e:print(f"捕获到 ZeroDivisionError:{e}")else:print("没有发生异常,执行 else 块")finally:print("执行 finally 块,无论是否发生异常")# 示例调用
test_unhandled_exception()
输出:
进入 try 块
执行 finally 块,无论是否发生异常
Traceback (most recent call last):File "example.py", line 12, in <module>test_unhandled_exception()File "example.py", line 4, in test_unhandled_exceptionresult = undefined_variable # 未定义的变量,触发 NameError
NameError: name 'undefined_variable' is not defined
解析:
try块中发生了NameError,但没有对应的except块捕获该异常。else块被跳过。finally块执行。- 异常向上传播,导致程序崩溃并打印堆栈跟踪信息。
建议
- 明确捕获异常类型:尽量只捕获您预期的异常,避免使用过于宽泛的
except Exception,以免掩盖其他潜在的问题。 - 处理未捕获的异常:如果需要,可以在最外层添加一个通用的异常处理,或者使用全局异常处理器,确保程序的健壮性。
- 测试代码逻辑:通过单元测试和异常测试,确保在各种情况下程序都能按预期运行。
常见的异常处理场景
在实际开发中,异常处理常见于以下场景:
- 网络请求:处理超时、连接错误等网络异常。
- 资源访问:解决文件不存在、权限不足等问题。
- 代码逻辑:捕获越界访问、
KeyError等逻辑错误。
示例:处理网络请求中的异常
import requestsdef fetch_data(url):try:response = requests.get(url, timeout=5)response.raise_for_status()except requests.exceptions.Timeout as e:print(f"请求超时:{e}")except requests.exceptions.HTTPError as e:print(f"HTTP 错误:{e}")except requests.exceptions.RequestException as e:print(f"发生网络错误:{e}")else:print("请求成功,处理响应数据。")return response.json()finally:print("请求结束。")# 示例调用
data = fetch_data("https://api.example.com/data")
在这个示例中,我们通过 try-except-else-finally 结构来处理网络请求中的各种潜在问题,如超时和其他网络错误。
为什么需要自定义异常?
在实际的开发中,标准库提供的异常类型并不能涵盖所有的业务需求。这时,自定义异常便显得尤为重要。通过自定义异常,您可以:
- 表达特定的业务逻辑错误。
- 附加更多的上下文信息,便于调试。
- 提高代码的可读性和维护性。
如何自定义异常?
自定义异常通常是继承自 Exception 类,而非 BaseException,这是因为 BaseException 包含了一些系统退出等行为,不适合普通业务逻辑使用。
示例:自定义异常
class MyBusinessError(Exception):def __init__(self, message, error_code):super().__init__(message)self.error_code = error_codedef process_data(data):if not isinstance(data, dict):raise MyBusinessError("数据格式无效", 1001)# 处理数据的逻辑if 'key' not in data:raise MyBusinessError("缺少必要的键:'key'", 1002)try:process_data("Invalid data")
except MyBusinessError as e:print(f"发生错误:{e},错误代码:{e.error_code}")
在此示例中,我们定义了一个 MyBusinessError 异常类,能够携带额外的错误代码,用于更加精准的错误处理。
拓展内容
1. 使用上下文管理器处理资源
Python 提供了 with 语句,用于管理资源的分配和释放。上下文管理器通过实现 __enter__ 和 __exit__ 方法,能够确保资源在使用后得到正确释放,避免资源泄漏的问题。
示例:使用上下文管理器处理文件操作
class FileManager:def __init__(self, filename, mode):self.filename = filenameself.mode = modeself.file = Nonedef __enter__(self):print("打开文件")self.file = open(self.filename, self.mode)return self.filedef __exit__(self, exc_type, exc_value, traceback):print("关闭文件")if self.file:self.file.close()if exc_type:print(f"发生错误:{exc_value}")return True # 抑制异常# 示例调用
with FileManager('example.txt', 'w') as f:f.write('Hello, world!')# 手动引发异常进行测试# raise ValueError("测试异常")
通过上下文管理器,我们可以简化资源管理,并在必要时捕获和处理异常。
2. 日志记录与异常处理
在生产环境中,仅仅捕获异常是不够的,记录异常信息对于排查问题至关重要。Python 的 logging 模块提供了一种灵活的方式来记录异常,支持将日志输出到控制台、文件或远程服务器。
示例:使用日志记录异常
import logginglogging.basicConfig(level=logging.ERROR,format='%(asctime)s %(levelname)s %(message)s',filename='app.log')def divide(a, b):try:return a / bexcept ZeroDivisionError as e:logging.error(f"尝试除以零:{e}")raise # 重新引发异常# 示例调用
try:divide(10, 0)
except ZeroDivisionError:print("捕获到一个异常!")
通过 logging 模块,我们不仅可以记录异常,还能保留详细的错误信息,以便后续分析。
3. 异常的链式处理
在处理异常时,有时需要保留原始异常的上下文信息,以便更好地理解错误的发生原因。Python 允许使用 raise ... from ... 语句来链式引发异常,从而保留原始异常的堆栈信息。
深入解析
当一个异常发生时,我们可能希望捕获它,然后抛出一个新的异常,但又不想丢失原始异常的信息。raise ... from ... 语句可以帮助我们实现这一点,它明确地将新的异常与原始异常关联起来。
示例:链式处理异常
class DataProcessingError(Exception):"""数据处理错误的自定义异常"""passdef process_data(data):try:result = int(data)except ValueError as e:# 使用 'from' 保留原始异常信息raise DataProcessingError("数据处理失败,无法转换为整数") from eelse:return result * 2# 示例调用
try:process_data("invalid")
except DataProcessingError as e:print(f"发生错误:{e}")# 通过 '__cause__' 属性访问原始异常print(f"原始异常:{e.__cause__}")
输出:
发生错误:数据处理失败,无法转换为整数
原始异常:invalid literal for int() with base 10: 'invalid'
应用场景
- 增加错误的语义化:使异常信息更贴近业务逻辑。
- 调试复杂问题:保留异常链有助于追踪问题的根源。
- 错误封装:在模块内部使用内部异常类型,对外部提供统一的异常接口。
4. 提高代码健壮性的最佳实践
在异常处理上,还有一些通用的最佳实践可以帮助提高代码的健壮性:
- 避免过度捕获:不要过度捕获所有异常,尤其是使用
except Exception:时。应尽可能捕获特定的异常,以免掩盖潜在的问题。 - 详细的错误信息:在抛出或记录异常时,尽量提供有用的上下文信息,帮助快速定位问题。
- 统一的异常处理策略:在大型项目中,使用统一的异常处理策略或中间件,确保异常处理的一致性。
5. 异常和单元测试
在编写单元测试时,测试代码是否能够正确处理异常是非常重要的。unittest 和 pytest 都提供了对异常进行测试的功能。
示例:使用 unittest 测试异常
import unittestdef divide(a, b):if b == 0:raise ValueError("不能除以零")return a / bclass TestDivision(unittest.TestCase):def test_divide_by_zero(self):with self.assertRaises(ValueError) as context:divide(10, 0)self.assertEqual(str(context.exception), "不能除以零")if __name__ == '__main__':unittest.main()
通过测试异常,我们可以确保代码在异常情况下的行为符合预期,从而提高代码的健壮性。
6. 全局异常处理
在某些应用程序中,您可能希望设置一个全局的异常处理器,捕获所有未处理的异常,避免应用程序崩溃。这个策略通常在 Web 应用程序或者 GUI 应用程序中非常有用。
深入解析
Python 提供了 sys.excepthook 函数,允许我们定义未捕获异常的处理方式。默认情况下,未捕获的异常会导致程序终止,并在控制台输出堆栈跟踪信息。通过自定义 sys.excepthook,我们可以控制异常的输出形式,或者在异常发生时执行特定的操作,例如日志记录、资源清理等。
示例:全局异常处理器
import sys
import tracebackdef global_exception_handler(exc_type, exc_value, exc_traceback):if issubclass(exc_type, KeyboardInterrupt):# 对于键盘中断,调用默认的异常处理sys.__excepthook__(exc_type, exc_value, exc_traceback)return# 打印自定义的错误信息print("捕获到未处理的异常:")print(f"类型:{exc_type.__name__}")print(f"值:{exc_value}")# 可选择将堆栈信息写入日志with open('error.log', 'a') as f:traceback.print_exception(exc_type, exc_value, exc_traceback, file=f)# 也可以在此处添加其他清理或通知操作# 设置全局异常处理器
sys.excepthook = global_exception_handler# 示例引发未捕获的异常
def faulty_function():return 1 / 0faulty_function()
输出:
捕获到未处理的异常:
类型:ZeroDivisionError
值:division by zero
注意事项
- 谨慎使用全局异常处理器:全局异常处理器可能会掩盖程序中的严重错误,导致问题难以被发现。
- 避免吞掉异常:在处理异常时,应确保记录足够的信息,并在必要时让程序继续抛出异常。
- 线程中的异常:对于多线程程序,线程中的异常不会触发
sys.excepthook,需要使用threading.excepthook(Python 3.8 及以上)或者在线程中手动捕获异常。
应用场景
- 日志记录:统一捕获未处理的异常,记录日志,便于问题排查。
- 用户反馈:在 GUI 应用中,捕获异常后给用户友好的提示,而不是程序崩溃。
- 资源清理:在异常发生时执行必要的资源释放或状态恢复操作。
7. 在 GUI 应用中使用全局异常处理
import sys
import tkinter as tk
from tkinter import messageboxdef global_exception_handler(exc_type, exc_value, exc_traceback):# 显示错误对话框messagebox.showerror("错误", f"发生未处理的异常:\n{exc_value}")# 可以在此处记录日志或执行其他操作sys.excepthook = global_exception_handler# 创建一个简单的 GUI 应用
def create_gui():root = tk.Tk()root.title("异常处理示例")def cause_exception():# 引发一个异常raise ValueError("这是一个示例异常")btn = tk.Button(root, text="引发异常", command=cause_exception)btn.pack(padx=20, pady=20)root.mainloop()create_gui()
在这个示例中,当用户点击按钮时,会引发一个异常。全局异常处理器会捕获该异常,并显示一个错误对话框,而不是让程序崩溃退出。
结论
Python 的异常处理机制是编写健壮代码的基础。通过掌握异常的层次结构、异常处理的格式、常见处理场景、自定义异常,以及一些高级技巧,您可以应对各种复杂的实际问题。
在实践中,请务必遵循 try-except-else-finally 结构来确保代码的稳定性,并深入理解各个代码块的执行顺序和条件。明确 else 块只在没有发生任何异常时执行,而未被捕获的异常不会进入 else 块,这有助于避免逻辑混淆。
同时,通过自定义异常和日志记录增强代码的可维护性。理解和正确使用异常的链式处理和全局异常处理,可以帮助您编写出更加健壮和专业的应用程序。
无论是网络请求、文件操作还是复杂的业务逻辑,正确的异常处理都是不可或缺的技能。希望本文的深入解析和示例能够帮助您更好地掌握 Python 异常处理的实战技巧,提升您的编程能力。
相关文章:
掌握 Python 异常处理的实战技巧:从基础到高级应用20240918
掌握 Python 异常处理的实战技巧:从基础到高级应用 引言 在 Python 编程中,异常处理是保障代码稳健性和可靠性的关键要素之一。无论是在网络请求、资源访问,还是复杂的业务逻辑中,异常处理都不可或缺。本文将从 Python 异常的基…...
One API 部署与配置指南
技术文档:One API 部署与配置指南 概述 One API 是一个多功能的 API 管理平台,支持自定义设置、用户管理、多种登录注册方式、主题切换等。本文档提供了详细的部署和配置指南,帮助用户快速搭建和使用 One API。 部署 基于 Docker 部署 D…...
国密起步7:BouncyCastle使用SM4自定义格式加解密C#版
初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的,可以在任何平台上使用。 github源码指引的指引-CSDN博…...
Qt优秀开源项目之二十三:QSimpleUpdater
QSimpleUpdater是开源的自动升级模块,用于检测、下载和安装更新。 github地址:https://github.com/alex-spataru/QSimpleUpdater QSimpleUpdater目前Star不多(911个),但已在很多开源项目看到其身影,比如Not…...
使用 Nmap 进行 SSL/TLS 加密套件枚举
1. Nmap 简介 Nmap(Network Mapper)是一个开源的网络探测和安全审计工具。它广泛用于扫描网络并发现设备、端口及服务,同时也支持多种脚本来进行更高级的安全扫描。Nmap 的 -sV 参数可以用于探测开放端口上的服务及版本信息,配合…...
探索 Python 的火焰:Fire 库的神秘力量
文章目录 🔥 探索 Python 的火焰:Fire 库的神秘力量第一部分:背景介绍第二部分:Fire 库是什么?第三部分:如何安装 Fire?第四部分:简单库函数使用方法第五部分:场景应用第…...
【Day14-单例设计模式动态代理】
单例设计模式 什么是设计模式(Design pattern) ? 一个问题通常有n种解法,其中肯定有一种解法是最优的,这个最优的解法被人总结出来了,称之为设计模式。设计模式有20多种,对应20多种软件开发中会遇到的问题…...
代码随想录训练营Day7 | 454.四数相加II | 383. 赎金信 | 15. 三数之和 | 18. 四数之和
代码随想录 (programmercarl.com) Leetcode 454. 四数相加 II 题目描述 给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] B[j] C[k] D[l] 0。 为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N&#…...
C++和OpenGL实现3D游戏编程【目录】
欢迎来到zhooyu的专栏。 个人主页:【zhooyu】 文章专栏:【OpenGL实现3D游戏编程】 贝塞尔曲面演示: 贝塞尔曲面演示zhooyu 本专栏内容: 我们从游戏的角度出发,用C去了解一下游戏中的功能都是怎么实现的。这一切还是要…...
03-Mac系统PyCharm主题设置
目录 1. 打开PyCharm窗口 2. Mac左上角点击PyCharm,点击Settings 3. 点击第一项Appearance& Behavior 4. 点击Appearance 5. 找到Theme进行设置 1. 打开PyCharm窗口 2. Mac左上角点击PyCharm,点击Settings 3. 点击第一项Appearance& Behavi…...
Java并发的四大定律
每一个进入 Java 并发世界的人,都会不可避免地面临一系列问题:线程安全、并发控制、锁,以及共享资源。这些概念复杂又抽象,往往让人无从下手。幸运的是,业界早已总结出一些法则,这些法则为我们处理并发问题…...
java项目之基于springboot的贸易行业crm系统(源码+文档)
风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的基于springboot的贸易行业crm系统。项目源码以及部署相关请联系风歌,文末附上联系信息 。 项目简介: 基于sp…...
General OCR Theory: Towards OCR-2.0 via a Unified End-to-end Model
摘要 传统的OCR系统(OCR-1.0)越来越无法满足人们对智能处理人造光学字符的需求。在本文中,我们将所有人造光学信号(例如,普通文本、数学/分子公式、表格、图表、乐谱,甚至是几何形状)统称为“字…...
二十种编程语言庆祝中秋节
二十种编程语言庆祝中秋节 文章目录 二十种编程语言庆祝中秋节中秋快乐!家人们 🥳一 Python二 C三 C四 Java五 C#六 Perl七 Go八 Asp九 PHP十 JavaScript十一 JavaScript HTML十二 Visual Basic十三 早期 VB十四 Visual C十五 Delphi十六 Shell十七 Cobo…...
202409012在飞凌的OK3588-C的核心板上使用Rockchip原厂的Buildroot点MIPI屏【背光篇】
202409012在飞凌的OK3588-C的核心板上使用Rockchip原厂的Buildroot点MIPI屏【背光篇】 2024/9/12 10:44 缘起,拿到一块MIPI屏,需要使用飞凌的OK3588-C的核心板在Android12下点亮。 在飞凌的Linux R4下修改部分屏参之后即可直接点亮。 但是在飞凌的Andro…...
DMDRS搭建
DMDRS搭建 本次进行DMDRS工具的部署搭建以及使用 环境配置 操作系统及数据库配置 操作系统:使用CentOS7数据库:dm8_20240408_x86_rh7_64 服务器配置 实例名服务器IPDM1192.168.19.7(源DMDRS)DM2192.168.19.4(目的…...
【油猴脚本】00006 案例 Tampermonkey油猴脚本自定义表格列名称,自定义表格表头,自定义表格的thead里的td
前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 【油…...
JS - 获取剪切板内容 Clipboard API
目录 1,需求最终效果 2,实现示例 3,注意点1,只支持安全上下文环境2,只能读取当前页面的剪切板3,权限获取问题4,获取内容的 MIME_TYPE 问题1,文本内容2,图片内容 5&#x…...
Qt自动打开文件夹并高亮文件
在Qt中,如果你想要打开一个文件夹并在文件管理器中高亮显示(选中)某个文件,你可以使用以下方法: 对于Windows系统,你可以使用QProcess来启动explorer命令,并带上/select,参数来高亮显示文件。以…...
神经网络-MNIST数据集训练
文章目录 一、MNIST数据集1.数据集概述2.数据集组成3.文件结构4.数据特点 二、代码实现1.数据加载与预处理2. 模型定义3. 训练和测试函数4.训练和测试结果 三、总结 一、MNIST数据集 MNIST数据集是深度学习和计算机视觉领域非常经典且基础的数据集,它包含了大量的手…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...
在golang中如何将已安装的依赖降级处理,比如:将 go-ansible/v2@v2.2.0 更换为 go-ansible/@v1.1.7
在 Go 项目中降级 go-ansible 从 v2.2.0 到 v1.1.7 具体步骤: 第一步: 修改 go.mod 文件 // 原 v2 版本声明 require github.com/apenella/go-ansible/v2 v2.2.0 替换为: // 改为 v…...
基于单片机的宠物屋智能系统设计与实现(论文+源码)
本设计基于单片机的宠物屋智能系统核心是实现对宠物生活环境及状态的智能管理。系统以单片机为中枢,连接红外测温传感器,可实时精准捕捉宠物体温变化,以便及时发现健康异常;水位检测传感器时刻监测饮用水余量,防止宠物…...
