有些时候我们需要打印方法的执行日志,最简单的方法就是在方法的开始写一个
logging,info("start")
在执行的最后写一个
logging,info("end")
但是这样未免显得代码有些臃肿,而且,不那么专业,
那么,类级别的装饰器就派上用场了。这个小伙伴可以帮你记录下每个方法的执行过程,让你知道你的程序在做什么,以及它是如何做到的。
当你使用这个装饰器时,它会跟着你的方法走,记录下每一步的日志。不仅如此,它还会在方法进入和退出时向你打个招呼,告诉你你的方法是如何被调用的。
下面是一个简单的例子,展示了如何使用类级别的装饰器来记录方法执行日志:
class PrintLog:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
with self:
result = self.func(*args, **kwargs)
return result
def __enter__(self):
print(f"Method {self.func.__name__} started. ")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print(f"Method {self.func.__name__} finished. ")
return False
@PrintLog
@staticmethod
def my_method(arg1, arg2):
return result
使用 with
语句可以在装饰器中实现上下文管理器,使得在方法执行过程中发生的异常能够被捕获并正确处理。在进入方法之前,with
语句会自动调用 __enter__()
方法,在方法执行完毕后,也会自动调用 __exit__()
方法。
在上下文管理器中,__exit__()
方法的返回值可以控制是否将异常返回到上层调用者。如果 __exit__()
方法返回 True
,则不会将异常返回到上层调用者,相当于告诉 Python 解释器异常已经被处理了,不需要继续抛出。如果返回 False
,则会将异常返回到上层调用者。
在 __exit__()
方法中,可以通过 exc_type
数来判断是否有异常发生。
可以参照下面这个处理方式。
def __exit__(self, exc_type, exc_value, traceback):
if exc_type is not None:
print(f"Method {self.func.__name__} failed: {exc_value} ")
else:
print(f"Method {self.func.__name__} finished. ")
return False
我之前写的例子是用类级别的装饰器来处理静态方法的,因为静态方法不需要访问实例变量和实例方法,所以更适合用类级别的装饰器来装饰。如果需要装饰实例方法,可以使用 __get__
方法来支持对实例方法的装饰。
def __get__(self, instance, owner):
return self.__class__(self.func.__get__(instance, owner))
OK,我把静态方法和实例方法的装饰器重新整理一下。
class PrintLog:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
with self:
result = self.func(*args, **kwargs)
return result
def __get__(self, instance,cls):
return self if instance is None else .__class__(self.func.__get__(instance, cls))
def __enter__(self):
print(f"Method {self.func.__name__} started. ")
return self
def __exit__(self, exc_type, exc_val, trace):
if exc_type is not None:
print(f"Method {self.func.__name__} failed: {exc_val}")
else:
print(f"Method {self.func.__name__} finished. ")
return False
这样的话,静态方法和实例方法都可以使用,
静态方法的执行顺序是: init call enter call exit
实例方法的执行顺序是: init get call enter call exit
※因为实例方法需要先得到一个实例。所以先执行get。
OK,希望都大家有所帮助。