Python 异常处理魔法手册:try - except 的终极艺术

Python 异常处理魔法手册:try - except 的终极艺术

编程文章jaq1232025-05-24 12:43:051A+A-

对话实录

小白:(崩溃)我的程序一遇到错误就崩溃,怎么办?

专家:(掏出魔法书)用 try - except,让程序优雅地处理错误

当我们写的python程序遇到意想不到的问题的时候(比如出现了bug),如果程序不做处理,程序就会异常停止,此时我们可以使用try语句来捕获这些异常,不会使程序异常终止。

异常处理基础三连击

1. 基本用法

在 Python 中,try块用于包含可能会引发异常的代码。当try块中的代码引发异常时,程序流程会立即跳转到对应的except块中进行处理。

try:
	num = int(input("请输入数字:"))
 	print(10 / num)

except ValueError:
 	print("输入的不是数字!")

except ZeroDivisionError:
 	print("不能除以0!")

except Exception as e:
 	print(f"{e}")

在这段代码中,int(input("请输入数字:"))尝试将用户输入转换为整数,如果用户输入的不是数字,就会引发ValueError异常,此时会执行第1个except块中的代码。而10 / num如果num为 0,会引发ZeroDivisionError异常,由第2个except块处理,最后如果不满足上述错误由第3个except模块处理所有错误。

专家提醒

1.先捕获具体异常,再捕获通用异常!如果先捕获通用异常Exception,那么它会捕获所有异常,导致具体异常的except块永远不会执行。

2.不能只写try语句,最少需要搭配except语句或者finally语句

3.except语句可以写多个

2. else 和 finally

else块在try块没有引发任何异常时执行,finally块无论try块是否有异常,都会执行。

try:
	result = 10 / 2

except ZeroDivisionError:
 	print("除零错误")

else:
 	print("计算成功:", result) # 无异常时执行

finally:
 	print("执行完毕") # 无论是否有异常都执行

这里10 / 2不会引发异常,所以else块中的代码会执行,最后finally块也会执行。

三大实战案例

案例 1:文件操作

在文件操作中,可能会遇到文件不存在或者没有权限读取的情况。

try:
  with open("data.txt") as f:
		content = f.read()

except FileNotFoundError:
	print("文件不存在!")

except PermissionError:
	print("没有读取权限!")

else:
	process(content)

with open("data.txt") as f尝试打开文件,如果文件不存在,会引发FileNotFoundError异常;如果没有权限,会引发PermissionError异常。若成功打开并读取文件,else块中的process(content)会对文件内容进行处理。

案例 2:API 请求

使用requests库进行 API 请求时,可能会遇到请求超时或其他错误。

import requests
try:
 	response = requests.get("https://api.example.com/data", timeout = 5)
 	response.raise_for_status() # 检查HTTP错误

except requests.Timeout:
 	print("请求超时!")

except requests.RequestException as e:
 	print(f"请求失败:{e}")

else:
 	print(response.json())

requests.get("
https://api.example.com/data", timeout = 5)尝试发送请求,若 5 秒内未得到响应,会引发requests.Timeout异常。response.raise_for_status()会检查 HTTP 状态码,如果状态码表示请求失败(如 404、500 等),会引发requests.RequestException异常。若请求成功,else块会打印响应的 JSON 数据。

案例 3:数据库操作

使用sqlite3进行数据库操作时,可能会遇到各种数据库相关的错误。

import sqlite3
try:
 	conn = sqlite3.connect("test.db")
 	cursor = conn.cursor()
 	cursor.execute("SELECT * FROM users")

except sqlite3.OperationalError as e:
	print(f"数据库错误:{e}")

else:
 	print(cursor.fetchall())

finally:
	conn.close()

sqlite3.connect("test.db")尝试连接数据库,如果连接失败或者执行 SQL 语句时出现操作错误,会引发sqlite3.OperationalError异常。若操作成功,else块会打印查询结果,最后无论是否有异常,finally块都会关闭数据库连接。

四大血泪陷阱

捕获所有异常

直接使用except:会捕获所有异常,包括SystemExit等系统级异常,这可能导致程序无法正常退出或出现难以调试的问题。

try:
	risky_code()

except: #  捕获所有异常,包括SystemExit
	print("出错了")

正确做法是捕获Exception,它捕获所有非系统异常。

try:
	risky_code()
except Exception as e: #  捕获所有非系统异常
 	print(f"错误:{e}")

忽略异常

在except块中直接使用pass会吞掉异常,导致错误信息丢失,难以排查问题。

try:
	risky_code()
except: #  吞掉异常
	pass

正确做法是记录错误并重新抛出,以便上层调用者处理。

try:
	risky_code()
except Exception as e:
	log_error(e) # 记录错误
	raise # 重新抛出

finally 中的 return

在函数中,如果finally块中有return语句,它会覆盖try块中的return。

def test():
    try:
	    return 1
    finally:
		  return 2 #  覆盖了try中的return
print(test()) # → 2

这通常不是预期的行为,应避免在finally块中使用return,除非确实有特殊需求。

专家工具箱

1. 自定义异常

当 Python 内置的异常类型不能满足需求时,可以自定义异常。

先看下我们平常用到的异常比如AssertionError,SyntaxError,ZeroDivisionError,NameError等,在python的自带文件builtins.py中类似如下定义:

每个异常类继承了父类Exception,看上去定义很简单。

下面我们通过同一个示例来定义一个自定义的异常类,并在程序中抛出异常。

举例:编写一个猜数字的小游戏,输入数字范围为1-100,当输入的数字不在该范围内时会抛出异常;猜对的时候程序停止;猜错了可以继续猜,不限制次数。

1 定义一个输入异常的类

class InputError(Exception):
""" Input failed."""

   def __init__(self, *args, **kwargs):
      pass

2 编写猜数字游戏,并通过raise语句抛出异常

def guess_number():
  #定义数字5
  num = 5
  while True:
     print(f'请输入的数字')
     #input函数输入默认为字符串
     number = int(input())  
     print(f'输入的数字为{number}')

     if number < 0 or number > 100:
        raise InputError
  
     if num == number:
        print(f'猜对了数字为{num},游戏结束')
        break
     else:
        print(f'没有猜对奥,请继续猜吧')

3 执行函数guess_number(),当我们在屏幕中输入数字-1或者101时,程序会抛出异常如下

4 通过try捕获自定义的异常

try:
   guess_number()
except InputError:
   print('输入的数字不能小于0,大于100')

当执行程序并重新输入数字101时,程序捕获到异常并打印如下:

2. 异常链

异常链可以在捕获一个异常后,引发另一个异常,并保留原始异常的信息。

try:
	risky_code()
except ValueError as e:
	raise RuntimeError("处理失败") from e

这样在捕获ValueError后,引发RuntimeError,并且通过from e保留了ValueError的信息,便于调试。

3. 上下文管理器

上下文管理器通过__enter__和__exit__方法来管理资源的分配和释放,在__exit__方法中可以处理异常。

class SafeOperation:
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type:
            print(f"捕获异常:{exc_val}")    
            return True # 抑制异常

with SafeOperation():
	risky_code()

当with块中的risky_code()引发异常时,__exit__方法会捕获并处理异常,return True表示抑制异常,不再向上层抛出。

小白:(献上膝盖)原来异常处理这么强大!

专家:(扶起小白)记住:异常处理不是万能的,但没有它是万万不能的!

点击这里复制本文地址 以上内容由jaq123整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

苍茫编程网 © All Rights Reserved.  蜀ICP备2024111239号-21