Python try-except异常捕获顺序与API重试实战
Python 的 try-except 是运行时错误的“安全气囊”:异常不处理,程序会中断;把可能出错的代码放进 try,并用 except 捕获指定异常,就能决定继续执行还是返回默认值。下面按语法、捕获顺序、实战重试和 JSON 解析几个方面整理。一、基本语法与最小示例
try 块放可能出错的代码,except 指定要匹配的异常类型。除零示例:
try:
result = 1 / 0
print(f'结果: {result}')
except ZeroDivisionError:
print('错误: 不能除以零!')
result = None
print('程序继续执行...')
print(f'result = {result}')
这里 ZeroDivisionError 被捕获后,后面的代码继续执行,result 被赋值为 None。若没有 try-except,程序会在 1 / 0 处崩溃。
安全获取数字输入时,可把 input 和 float 转换放在 try 中,循环直到成功:
def get_number(prompt='请输入数字: '):
while True:
try:
value = input(prompt)
return float(value)
except ValueError:
print(f'{value}不是一个有效的数字,请重新输入')
ValueError 对应 float 转换失败。注意 value 在输入后才有值,因此 except 中可引用它。
读取配置文件时,文件不存在和 JSON 格式错误可以分别处理:
import json
def read_config(filepath):
try:
with open(filepath, 'r', encoding='utf-8') as f:
return json.load(f)
except FileNotFoundError:
print(f'配置文件{filepath}不存在,使用默认配置')
return {'debug': False, 'host': 'localhost'}
except json.JSONDecodeError:
print(f'配置文件{filepath}格式错误,使用默认配置')
return {'debug': False, 'host': 'localhost'}
二、一次捕获多种异常
一个 try 可以跟多个 except,每种异常走不同分支。下面的 safe_operation 依次可能遇到 IndexError、KeyError、TypeError:
def safe_operation(data, index, key):
try:
value = data
result = value
return result
except IndexError:
print(f'索引{index}超出范围(数据长度={len(data)})')
return None
except KeyError:
print(f'键{key}不存在')
return None
except TypeError as e:
print(f'类型错误: {e}(data[{index}]不是字典)')
return None
data = [{'a': 1}, {'b': 2}]
print(safe_operation(data, 1, 'b'))
print(safe_operation(data, 5, 'a'))
print(safe_operation(data, 0, 'z'))
print(safe_operation(data, 0, 'a'))
调用结果依次为 2、索引越界返回 None、键不存在返回 None、正常返回 1。
三、捕获顺序:子类必须放在父类之前
except 按书写顺序匹配,一旦匹配成功,后面的 except 不再执行。因此更具体的异常要放在更宽泛的异常之前:
def good_order():
try:
result = 1 / 0
except ZeroDivisionError:
print('除以零错误')
except ArithmeticError:
print('算术错误')
except Exception:
print('其他异常')
good_order()
如果先写 except Exception,再写 except ZeroDivisionError,后者永远匹配不到。因为 ZeroDivisionError 是 ArithmeticError 等父类的子类。
裸 except 会捕获包括 KeyboardInterrupt 在内的系统异常,通常不推荐:
try:
pass
except:
pass
至少写成 except Exception as e,并记录日志或回滚;更好的做法是只捕获你能处理的异常,例如:
try:
pass
except (ValueError, TypeError) as e:
print(f'数据错误: {e}')
except IOError as e:
print(f'I/O错误: {e}')
四、实战:API 调用重试
网络调用可能因 ConnectionError 失败,适合有限次重试。下例参数 max_retries=3、delay=1;ConnectionError 会等待后重试,ValueError 表示参数错误,直接 raise 不重试;全部失败后抛出 ConnectionError:
import time
import random
def call_api_with_retry(url, max_retries=3, delay=1):
last_error = None
for attempt in range(max_retries):
try:
if random.random() < 0.7:
raise ConnectionError(f'连接超时 (尝试{attempt+1})')
return f'API响应: 数据来自{url}'
except ConnectionError as e:
print(f'⚠ {e},{delay}秒后重试...')
last_error = e
time.sleep(delay)
except ValueError as e:
print(f'✗ 参数错误: {e}')
raise
raise ConnectionError(f'API调用失败(重试{max_retries}次后): {last_error}')
try:
result = call_api_with_retry('https://api.example.com/data')
print(f'✓ {result}')
except ConnectionError as e:
print(f'✗ 最终失败: {e}')
重试适合可恢复错误;参数错误重试无意义,所以 ValueError 直接向上传播。
五、实战:安全解析 JSON
json.loads 可能抛 JSONDecodeError,输入为 None 或空字符串也应处理。下例返回 default,并利用异常对象的 lineno、colno、pos 打印定位信息:
import json
def safe_json_parse(json_string, default=None):
if default is None:
default = {}
if json_string is None:
print('输入为None,返回默认值')
return default
if not json_string.strip():
print('输入为空字符串,返回默认值')
return default
try:
return json.loads(json_string)
except json.JSONDecodeError as e:
print(f'JSON解析错误: {e}')
print(f' 位置: 第{e.lineno}行, 第{e.colno}列')
print(f' 内容片段: {json_string}')
return default
except TypeError as e:
print(f'类型错误: {e}')
return default
print(safe_json_parse('{"name": "张三", "age": 25}'))
print(safe_json_parse('{invalid json}'))
print(safe_json_parse(None))
合法 JSON 返回解析后的对象;非法 JSON 返回 default,并输出错误位置;None 直接返回 default。
六、总结
1. try 中放可能出错的代码,except 只匹配指定类型。
2. except 顺序是具体在前、宽泛在后,否则子类分支可能永远不会执行。
3. 不要使用裸 except,至少用 except Exception 并记录;能处理具体异常时优先具体异常。
4. 每个 except 只处理一种或一类异常,不同异常不同处理。
5. 只有你能“做点什么”时才捕获,否则让异常继续向上传播。异常处理的本质是责任转移:函数无法完成职责时,通过异常告知调用者。
以上代码和规则可用于脚本、接口调用、配置读取、数据解析等场景。核心不是把所有错误吞掉,而是让可恢复错误有出路,让不可处理错误保留现场。
Re: Python try-except异常捕获顺序与API重试实战
写得很实用,尤其是异常捕获顺序那段。很多人会把 except Exception 放前面,结果后面的具体异常永远匹配不到,这点提醒得很到位。安全输入数字和读取配置文件的例子也很贴近日常脚本,分开处理 FileNotFoundError 和 JSONDecodeError 确实比一把抓清楚。 API 重试部分思路也很清晰,ConnectionError 做有限重试、ValueError 直接抛出,这个区分很关键,因为参数错误重试多少次都没意义。如果后续补全,我会再加两个小点:每次重试时把 attempt 和 last_error 记到日志里,方便排查;最后一次失败后不要再等待,避免白等一个 delay。整体看下来,对刚写 Python 脚本的人挺有帮助。Re: Python try-except异常捕获顺序与API重试实战
楼主这篇整理得挺清楚的,基本语法、多个 except、捕获顺序和重试实战都覆盖到了。我平时写 try-except 最容易踩的也是顺序问题,把 Exception 放前面之后,具体异常就形同虚设,这点确实值得反复强调。裸 except 也要少用,至少写成 except Exception as e 并记日志,不然 KeyboardInterrupt 这类系统异常也会被吞掉。 配置文件读取那段分开处理 FileNotFoundError 和 JSONDecodeError 很实用,返回默认配置能让程序继续跑。API 重试里只对 ConnectionError 重试、对 ValueError 直接抛出,这个划分很合理;实际用的时候再加上有限次数和等待间隔会更稳。不过你最后重试函数的代码好像没贴完,期待补上完整版本,尤其想看看全部失败后是怎么把 last_error 抛出去的。 整体看下来对新手很友好,例子也能直接改着用。感谢分享。Re: Python try-except异常捕获顺序与API重试实战
整理得挺系统的,尤其把 except 的匹配顺序讲清楚了,子类放在父类前面这点很容易踩坑,一旦先写 Exception,后面具体异常就基本失效了。安全输入和配置文件解析的例子也很实用,FileNotFoundError 跟 JSONDecodeError 分开处理,比一股脑捕获清楚得多。 API 重试部分的思路也对,有限次数、区分可重试异常和参数错误很重要。实际写的时候建议再加一点退避和日志,比如每次失败记录 attempt 和异常原因,delay 可以随次数增加,避免连续重试把服务压垮;只重试 ConnectionError 这类临时故障,ValueError 直接抛出不重试是合理的。整体很适合做脚本异常处理的参考。
页:
[1]