Python子类未重写基类方法时,装饰器如何显示子类方法限定名?
问题解决思路
核心问题在于:基类方法被子类实例调用时,装饰器内的func仍是基类的函数对象,导致func.__qualname__始终指向基类方法。要获取实际调用的子类限定名,需从方法的第一个参数(实例self或类cls)中提取当前类的信息。
修改后的装饰器代码
import functools import logging def log(_func=None, *, logger): def decorator_log(func): @functools.wraps(func) def wrapper(*args, **kwargs): # 动态获取当前调用的类限定名+方法名 current_qualname = func.__qualname__ if args: first_arg = args[0] # 处理实例方法(第一个参数是self) if hasattr(first_arg, '__class__'): current_qualname = f"{first_arg.__class__.__qualname__}.{func.__name__}" # 处理类方法(第一个参数是cls) elif isinstance(first_arg, type): current_qualname = f"{first_arg.__qualname__}.{func.__name__}" try: logger.info(f"Entering function {current_qualname}") args_repr = [repr(a) for a in args] kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()] signature = ", ".join(args_repr + kwargs_repr) logger.debug(f"function {current_qualname} called with args {signature}") result = func(*args, **kwargs) logger.info(f"Leaving function {current_qualname}") return result except Exception as e: logger.exception(f"Exception raised in {current_qualname}. exception: {str(e)}") raise return wrapper if _func is None: return decorator_log else: return decorator_log(_func)
关键修改说明
动态生成限定名:
检查方法的第一个参数:如果是实例(self),则用self.__class__.__qualname__获取子类的限定名;如果是类(cls,类方法场景),直接用cls.__qualname__。无有效第一个参数时, fallback 到原func.__qualname__,兼容静态方法和普通函数。修复不可达代码:
原装饰器中logger.info("Leaving...")位于return/raise之后,永远无法执行。现在调整到方法正常执行后的return前,确保离开日志能输出。优化异常处理:
移除外层冗余的try-except(原代码会隐藏装饰器自身错误),仅保留业务方法的异常捕获,保证异常能正常向上抛出。
使用验证示例
基类定义
class BaseController: logger = logging.getLogger(__name__) @log(logger=logger) def initialize_components(self): print("Base initialize")
子类定义(未重写基类方法)
class MainController(BaseController): logger = logging.getLogger(__name__) def __init__(self): pass # 测试调用 if __name__ == "__main__": logging.basicConfig(level=logging.INFO) mc = MainController() mc.initialize_components()
输出结果
INFO:__main__:Entering function MainController.initialize_components Base initialize INFO:__main__:Leaving function MainController.initialize_components
可见,即使子类未重写基类方法,日志仍会显示子类的限定名MainController.initialize_components。
内容的提问来源于stack exchange,提问作者KBriggs
相关产品推荐
相关产品推荐

