如何为可迭代对象添加异常处理装饰器?寻求更优Python实现
更简洁的Pythonic实现方案
你可以用一个统一的包装器处理两类函数,无需单独判断是否为生成器函数。核心思路是:先捕获函数调用阶段的异常,再通过yield from统一处理迭代过程中的异常——不管原函数返回的是列表还是生成器/迭代器,yield from都能遍历其元素,同时捕获迭代时的异常。
from functools import wraps def failing_generator(): for i in range(1, 5): if i % 2 == 0: print('I dont like even numbers.') raise ValueError(i) yield i def bad_list(): lst = [] for i in range(1, 5): if i % 2 == 0: raise ValueError(i) lst.append(i) return lst def catch_iter_exceptions(fn): @wraps(fn) def wrapper(*args, **kwargs): # 捕获函数调用时的异常(比如返回列表的函数构建列表时抛出的异常) try: iterable = fn(*args, **kwargs) except ValueError as err: print('Not to worry.') return # 捕获迭代过程中的异常(生成器/迭代器遍历阶段的异常) try: yield from iterable except ValueError as err: print('Not to worry.') return wrapper # 测试生成器函数 print("测试生成器函数:") for x in catch_iter_exceptions(failing_generator)(): print(x) # 测试返回列表的函数 print("\n测试返回列表的函数:") for x in catch_iter_exceptions(bad_list)(): print(x)
运行结果
测试生成器函数: 1 I dont like even numbers. Not to worry. 测试返回列表的函数: Not to worry.
方案优势
- 代码更简洁:无需借助
isgeneratorfunction判断函数类型,一个包装器统一处理两类场景。 - 符合Python风格:利用
yield from的特性,自然遍历任何可迭代对象,同时统一捕获迭代阶段的异常。 - 兼容性强:不管原函数返回列表、生成器还是其他迭代器(比如
iter()创建的迭代器),都能正确捕获异常。
如果需要保持原函数的返回类型(比如原函数返回列表,包装后也返回列表而非生成器),可以在迭代完成后将元素收集为列表返回,但这样会失去迭代器的惰性特性。如果你的场景允许返回生成器(毕竟都是可迭代对象),上面的方案是最优选择。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

