如何实现可根据被装饰函数执行自定义异常后续操作的装饰器?
实现支持自定义异常处理逻辑的装饰器
你的原装饰器只能统一执行日志记录,要实现针对不同函数执行自定义异常操作(比如回滚),可以通过以下两种实用方案改造:
方案1:带参数的装饰器(推荐)
让装饰器接受一个自定义异常处理函数作为参数,这样在装饰不同函数时,可以传入对应的操作逻辑(比如回滚、资源清理等)。
改造后的装饰器代码
import logging # 默认异常处理:仅记录日志 def default_error_handler(func, exc): logging.error('Error in "{}" function: {}'.format(func.__name__, str(exc))) def exception_handler(error_handler=default_error_handler): def decorator(func): def inner_function(*args, **kwargs): try: output = func(*args, **kwargs) return output except Exception as exc: # 调用传入的自定义处理函数,传递被装饰函数和异常对象 error_handler(func, exc) return inner_function return decorator
使用示例
比如给数据库操作函数绑定回滚逻辑,给普通函数保留默认日志:
# 自定义数据库回滚处理 def db_rollback_handler(func, exc): logging.error('DB operation failed in "{}": {}'.format(func.__name__, str(exc))) # 这里替换为实际的回滚代码,比如: # db_connection.rollback() print(f"[ACTION] Executing rollback for {func.__name__}") @exception_handler(error_handler=db_rollback_handler) def update_user_db(user_id, new_data): # 模拟数据库操作抛出异常 raise ValueError("Invalid user data format") # 使用默认日志处理的函数 @exception_handler() def sum_2_numbers(x, y): return x + y # 测试 update_user_db(1, {"name": ""}) sum_2_numbers(3, 'a')
方案2:利用函数属性指定处理逻辑
不需要修改装饰器的调用语法,直接给被装饰函数添加一个自定义属性(比如on_exception),装饰器捕获异常时检查并执行该属性对应的逻辑。
改造后的装饰器代码
import logging def exception_handler(func): def inner_function(*args, **kwargs): try: output = func(*args, **kwargs) return output except Exception as exc: # 检查函数是否有自定义异常处理属性 if hasattr(func, 'on_exception') and callable(func.on_exception): func.on_exception(func, exc) else: # 无自定义逻辑时执行默认日志 logging.error('Error in "{}" function: {}'.format(func.__name__, str(exc))) return inner_function
使用示例
@exception_handler def update_user_db(user_id, new_data): raise ValueError("Invalid user data format") # 给函数绑定自定义回滚逻辑 def db_rollback(func, exc): logging.error('Rollback triggered for "{}": {}'.format(func.__name__, str(exc))) print(f"[ACTION] Rolling back DB changes for {func.__name__}") update_user_db.on_exception = db_rollback @exception_handler def sum_2_numbers(x, y): return x + y # 测试 update_user_db(1, {}) sum_2_numbers(3, 'a')
方案对比
- 带参数的装饰器:可读性更强,适合在装饰函数时就明确异常处理逻辑的场景,代码结构更清晰。
- 函数属性方式:灵活性更高,支持后续动态修改异常处理逻辑,无需改动装饰器语法。
内容的提问来源于stack exchange,提问作者mat
相关产品推荐
相关产品推荐

