以编程方式重写父类方法:Python自定义Logger兼容字符串与Message对象
问题分析与解决方案
批量重写出错的常见原因
你遇到的TypeError大概率是两个问题导致的:
- 包装函数缺少
self参数:如果批量生成方法时用了lambda或者定义函数时没把self作为第一个参数,调用日志方法时,实例对象会被当作msg参数传入,真正的日志内容变成了后续参数,自然会提示缺少msg。 - 闭包变量延迟绑定:循环批量生成方法时,若直接引用循环变量(比如
level),所有包装方法会共享这个变量的最终值,导致调用时触发错误的日志级别方法,也可能间接引发参数不匹配的问题。
替代方案:重写底层_log方法(推荐)
Python原生logging的所有日志级别方法(debug/info/warning等)最终都会调用_log这个底层方法,只需要重写这一个方法,就能实现所有级别同时支持字符串和Message对象,比批量重写更简洁可靠:
import logging class Message: # 模拟公司日志库的Message类 def __init__(self, content): self.content = content class CustomLogger(logging.Logger): def _log(self, level, msg, args, exc_info=None, extra=None, stack_info=False, stacklevel=1): # 自动将字符串转为Message对象 if isinstance(msg, str): msg = Message(msg) # 调用父类的底层日志方法 super()._log(level, msg, args, exc_info, extra, stack_info, stacklevel) # 使用示例 logging.setLoggerClass(CustomLogger) logger = logging.getLogger(__name__) logger.info("普通字符串") # 自动转为Message对象 logger.debug(Message("直接传入Message")) # 正常处理
修复批量重写的正确写法
如果你坚持要用批量重写的方式,需要确保包装函数正确接收self,并通过闭包捕获正确的日志级别:
import logging from functools import wraps class Message: def __init__(self, content): self.content = content class CustomLogger(logging.Logger): pass def create_log_wrapper(level): @wraps(getattr(CustomLogger, level)) def wrapper(self, msg, *args, **kwargs): if isinstance(msg, str): msg = Message(msg) # 调用父类对应级别的方法 getattr(super(CustomLogger, self), level)(msg, *args, **kwargs) return wrapper # 批量为每个日志级别绑定包装方法 for level in ['debug', 'info', 'warning', 'error', 'critical']: setattr(CustomLogger, level, create_log_wrapper(level)) # 使用示例 logging.setLoggerClass(CustomLogger) logger = logging.getLogger(__name__) logger.warning("测试字符串") logger.error(Message("测试Message对象"))
这里用create_log_wrapper函数来捕获每个循环的level值,避免闭包延迟绑定的问题;同时用@wraps保留原方法的元信息,保证日志方法的签名和原生一致。
内容的提问来源于stack exchange,提问作者Leitouran
相关产品推荐
相关产品推荐

