我正在尝试构建一个装饰器,它允许我在 Python3 的类中记录函数。装饰器如下:
import functools
def log(_func=None, *, logger):
def decorator_log(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
logger.info(f"Entering function {func.__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 {func.__qualname__} called with args {signature}")
try:
result = func(*args, **kwargs)
return result
except Exception as e:
logger.exception(f"Exception raised in {func.__qualname__}. exception: {str(e)}")
raise e
logger.info(f"Leaving function {func.__qualname__}.{func.__name__}")
except Exception:
pass
return wrapper
if _func is None:
return decorator_log
else:
return decorator_log(_func)
然后用它来装饰类函数,如下所示:
class MainController(BaseController):
logger = logging.getLogger(__name__)
def __init__(self, main_model, main_view):
...
@log(logger=logger)
def initialize_components(self):
...
等等
这工作正常,但当我使用基类和子类时会遇到问题。特别是,我想装饰基类和子类中的几乎所有函数,但是当我这样做时,只有当子类重写基类方法时,
func.__qualname__
才会是subclass.methodname
,并且它将是baseclass.methodname
否则。
如何修改此方法,以便即使在子类不重写基类方法的情况下,应用于基类方法的装饰器也会在装饰器调用
subclass.methodname
的地方给我 func.__qualname__
,而不是基类?
我不想在类级别装饰它,因为在某些情况下,标准库类有多个继承级别,我只想装饰我自己定义的函数。
不用
func.__qualname__
,只需使用类名和 __name__
属性:
def wrapper(*args, **kwargs):
self = args[0] # this will only work for methods
...
name = "self.__class__.__name__}.{func.__name__}"
logger.debug(f"function {name} called with args {signature}")
...
当然,如果您希望将装饰器应用于“独立”函数,那么第一个参数(
args[0]
,如果它存在)将不会是对实例的引用:您必须调整上面的代码来检查这一点,相应地计算 name
。