登录
首页 >  文章 >  python教程

带参数的Logger异常捕获装饰器实现

时间:2026-05-25 10:14:26 190浏览 收藏

本文深入剖析了Python中带参数的类方法装饰器(如@Logger.catch(file_path='log.txt'))的正确实现原理,直击开发者常踩的“TypeError: missing 1 required positional argument 'func'”陷阱——根源在于混淆了装饰器工厂与实际装饰逻辑。文章通过清晰的三层闭包结构(参数接收 → 装饰器返回 → 函数包装),结合@staticmethod的巧妙运用和健壮的异常捕获、日志记录与文件写入处理,手把手教你构建可配置、可复用、不静默失败的生产级日志装饰器,并附有完整可运行示例和关键避坑指南,让复杂装饰器从此不再令人头疼。

本文详解如何在Python中正确实现带参数的类方法装饰器(如@Logger.catch(file_path='log.txt')),解决因未区分装饰器工厂与实际装饰逻辑导致的TypeError: missing 1 required positional argument 'func'错误。

在Python中,当需要为装饰器传入配置参数(如日志文件路径)时,必须采用装饰器工厂(decorator factory) 模式:外层函数接收参数并返回真正的装饰器,内层装饰器再接收被装饰的函数。直接将func作为@classmethod或普通实例方法的首个参数会导致调用时参数错位——正如报错所示:Logger.catch(file_path=...) 被解释为调用类方法,却未传入func,因此抛出 TypeError。

正确的实现方式是使用 @staticmethod 将 catch 定义为静态方法,并构建三层嵌套结构:

  • 第一层(catch):接收装饰器参数(如 file_path),返回第二层函数;
  • 第二层(decorator):接收被装饰的函数 func,返回第三层包装器;
  • 第三层(wrapper):执行实际逻辑(try/except + 日志记录)。

以下是修正后的完整 Logger 类示例:

import sys
import traceback

class Logger:
    @staticmethod
    def catch(file_path=None):
        def decorator(func):
            def wrapper(*args, **kwargs):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    _, _, tb = sys.exc_info()
                    traceback_info = traceback.extract_tb(tb)
                    if traceback_info:
                        last_frame = traceback_info[-1]
                        line = last_frame.lineno
                        file = last_frame.filename
                        function_name = last_frame.name
                    else:
                        line = file = function_name = "unknown"

                    # 假设 Logger.error 已正确定义(需确保其支持 file_path 参数)
                    Logger.error(
                        log_msg=str(e),
                        highlighting=True,
                        print_out=True,
                        line=[line, file, function_name],
                        file_path=file_path
                    )
                    raise  # 重新抛出异常,避免静默失败
            return wrapper
        return decorator

    @staticmethod
    def error(log_msg, highlighting=False, print_out=False, line=None, file_path=None):
        # 示例:基础错误日志输出逻辑(请按需完善)
        msg = f"[ERROR] {log_msg}"
        if line and len(line) == 3:
            msg += f" | File \"{line[1]}\", line {line[0]}, in {line[2]}"

        if print_out:
            print(msg)

        if file_path:
            try:
                with open(file_path, "a") as f:
                    f.write(msg + "\n")
            except OSError as write_err:
                print(f"[WARNING] Failed to write to log file {file_path}: {write_err}")

关键要点总结

  • ❌ 错误写法:def catch(func, file_path=None) → func 被当作必需位置参数,无法支持 @catch(file_path=...) 语法;
  • ✅ 正确模式:@staticmethod + 三阶闭包(参数 → 函数 → 包装器);
  • ⚠️ 注意事项:wrapper 中应 raise 异常以保持原有行为;Logger.error 方法需兼容 file_path 参数并处理文件写入异常;
  • ? 调试提示:若 traceback.extract_tb(tb) 返回空列表,请确保异常确实发生在 func 内部,且未被中间层捕获。

现在,你的装饰器即可安全使用:

@Logger.catch(file_path="tests/example.log")
def example_function():
    var1 = 5
    var = 0
    result = var1 / var  # 触发 ZeroDivisionError,自动记录到指定日志文件

理论要掌握,实操不能落!以上关于《带参数的Logger异常捕获装饰器实现》的详细介绍,大家都掌握了吧!如果想要继续提升自己的能力,那么就来关注golang学习网公众号吧!

资料下载
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>