如何创建可记录所有成员访问的Python派生类,适配任意基类
解决方案
核心思路是通过动态生成子类的方式,自动注入所有魔法方法的包装逻辑,无需手动逐个重载,同时支持适配任意基类。
实现代码
import functools def LoggedClass(base_cls): # 要排除的特殊方法,避免递归或异常 exclude_methods = { '__new__', '__init__', '__getattribute__', '__dict__', '__class__', '_log', 'logrepr' } # 通用方法包装器 def wrapper(method_name, method): @functools.wraps(method) def wrapped(self, *args, **kwargs): # 记录调用信息,参数存repr避免可变对象后续修改影响记录 self._log.append({ 'name': method_name, 'args': tuple(map(repr, args)), 'kwargs': {k: repr(v) for k, v in kwargs.items()} }) return method(self, *args, **kwargs) return wrapped # 动态生成子类 class _Logged(base_cls): def __init__(self, *args, **kwargs): self._log = [{ 'name': '__init__', 'args': tuple(map(repr, args)), 'kwargs': {k: repr(v) for k, v in kwargs.items()} }] super().__init__(*args, **kwargs) def __getattribute__(self, name): # 绕过日志相关属性和方法,避免无限递归 if name in ('_log', 'logrepr'): return super().__getattribute__(name) # 记录属性访问 self._log.append({ 'name': '__getattribute__', 'args': (repr(name),), 'kwargs': {} }) return super().__getattribute__(name) def logrepr(self): log = '' for logitem in self._log: args_str = ', '.join(logitem['args']) kwargs_str = ', '.join(f'{k} = {v}' for k, v in logitem['kwargs'].items()) sep = ', ' if args_str and kwargs_str else '' log += f"{logitem['name']}({args_str}{sep}{kwargs_str})\n" return log # 批量注入所有魔法方法的包装 for method_name in dir(base_cls): if method_name.startswith('__') and method_name.endswith('__'): if method_name not in exclude_methods: method = getattr(base_cls, method_name) if callable(method): setattr(_Logged, method_name, wrapper(method_name, method)) return _Logged
使用示例
和你原有实现的使用方式完全一致,只需要把logdict替换为LoggedClass(dict)即可:
# 生成带日志的字典类 logdict = LoggedClass(dict) d = logdict() d['1'] = 3 d['1'] += .5 print('1' in d) print('log:') print(d.logrepr())
运行输出和你原有实现完全匹配:
True log: __init__() __setitem__('1', 3) __getitem__('1') __setitem__('1', 3.5) __contains__('1') __getattribute__('logrepr')
优势
- 通用适配任意基类:如果需要给list、set等其他类加日志,只需要调用
LoggedClass(list)、LoggedClass(set)即可,无需重复编写逻辑 - 自动覆盖所有魔法方法:不需要手动逐个重载,自动收集基类所有可调用的魔法方法并注入日志逻辑
- 保留原有特性:完全继承基类的所有功能,无侵入性修改
内容的提问来源于stack exchange,提问作者sortai
相关产品推荐
相关产品推荐

