本文详解如何修复因装饰器参数设计不当导致的“missing 1 required positional argument”错误,通过静态方法+闭包组合构建可传参的类内装饰器,适用于日志捕获、异常处理等场景。
本文详解如何修复因装饰器参数设计不当导致的“missing 1 required positional argument”错误,通过静态方法+闭包组合构建可传参的类内装饰器,适用于日志捕获、异常处理等场景。
在 Python 中,为类定义带参数的装饰器(如 @Logger.catch(file_path='...'))时,常见的误区是将装饰器实现为普通实例方法或类方法——这会导致 Python 尝试将被装饰函数自动作为第一个参数传入,从而与显式声明的 file_path 参数冲突,引发 TypeError: Logger.catch() missing 1 required positional argument: 'func'。
根本原因在于:
装饰器调用语法 @Logger.catch(file_path=...) 实际上先执行 Logger.catch(file_path=...),期望其返回一个真正的装饰器(即接收 func 的函数),而非直接尝试装饰
。而原代码中 catch(func, file_path=None) 是一个两参数方法,当仅传入 file_path 时,func 无处可来,自然报错。
✅ 正确解法是采用
三层嵌套结构 + @staticmethod
:
第一层:接收装饰器参数(如 file_path),返回第二层函数;
第二层:接收被装饰函数 func,返回第三层 wrapper;
第三层:实际执行逻辑(含 try/except、日志记录等);
同时,必须将 catch 声明为 @staticmethod,避免隐式绑定 self 或 cls,确保调用链纯净。
立即学习
“
Python免费学习笔记(深入)
”;
Python 3.14.3
微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。
下载
以下是修复后的完整 Logger 类示例(含健壮性增强):
使用方式保持不变,且完全兼容:
⚠️ 注意事项:
不要省略 raise —— 否则异常会被吞掉,不利于调试;
file_path 应支持 None(默认不写文件)并做异常防护,避免因磁盘满/权限不足导致日志功能拖垮主逻辑;
若需支持异步函数,需额外实现 async def wrapper(...) 并用 await func(...),本例为同步场景;
@staticmethod 是关键:若误用 @classmethod,cls 会抢占第一个参数位;若无任何装饰器,则 self 会强制注入。
通过这种模式,你不仅能解决当前崩溃问题,还能复用该结构开发任意带配置参数的装饰器(如重试、超时、性能分析等),大幅提升工具类的灵活性与工程化水平。
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_no = last_frame.lineno
filename = last_frame.filename
func_name = last_frame.name
else:
line_no = filename = func_name = "unknown"
# 调用统一错误日志方法(需自行实现)
Logger.error(
log_msg=str(e),
highlighting=True,
print_out=True,
line=[line_no, filename, func_name],
file_path=file_path
)
raise # 重新抛出异常,避免静默失败
return wrapper
return decorator
@staticmethod
def error(log_msg, highlighting, print_out, line, file_path=None):
# 示例:简单控制台输出 + 可选文件写入
msg = f"[ERROR] {log_msg} | File: {line[1]}, Line: {line[0]}, Func: {line[2]}"
if print_out:
print(f"\033[91m{msg}\033[0m" if highlighting else msg)
if file_path:
try:
with open(file_path, "a", encoding="utf-8") as f:
f.write(f"{msg}\n")
except OSError as write_err:
print(f"[WARN] Failed to write log to {file_path}: {write_err}")@Logger.catch(file_path="tests/example.log")
def example_function():
var1 = 5
var = 0
result = var1 / var # 触发 ZeroDivisionError
print(result)
example_function() # 输出错误信息并写入日志文件