之前我们用函数方式写了一个装饰器: https://blog.csdn.net/a545578125/article/details/79812878 但是Graham Dumpleton 和 Lennart Regebro 两位大神认为,装饰器最好通过实现 __call__ 方法的类实现,不应该通过函数实现。
下面我们就来尝试一下用类实现装饰器:
import time
import functools
class T1:
def __init__(self, name):
self.name = name
def __call__(self, func):
print('I am outer!')
@functools.wraps(func)
def dec(*args, **kwargs):
t0 = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - t0
name = func.__name__
arg_lst = []
if args:
arg_lst.append(', '.join(repr(arg) for arg in args))
if kwargs:
pairs = ['%s=%r' % (k, w) for k, w in sorted(kwargs.items())]
arg_lst.append(', '.join(pairs))
arg_str = ', '.join(arg_lst)
print('[%0.8fs] %s(%s) -> %r ' % (elapsed, name, arg_str, result))
return dec
@T1('test')
def t2():
l = [1, 2, 3]
ff = [x*x for x in l]
print(ff)
t2()
输出:
I am outer!
[1, 4, 9]
[0.00000000s] t2() -> None
这里其实很好理解,因为T1('test')是T1的一个实例化对象,而这个对象因为实现了__call__方法,所以可以调用,这就和函数基本一样了。 也就是说,上述代码可以将@T1('test')改为 :
obj = T1('test')
@obj
此处 T1('test') 中的 test 其实可以是任何字符串,因为这里只是进行了一次实例化,给self.name赋值。而不影响此实例的__call__方法。
完整代码:
import time
import functools
class T1:
def __init__(self, name):
self.name = name
def __call__(self, func):
print('I am outer!')
@functools.wraps(func)
def dec(*args, **kwargs):
t0 = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - t0
name = func.__name__
arg_lst = []
if args:
arg_lst.append(', '.join(repr(arg) for arg in args))
if kwargs:
pairs = ['%s=%r' % (k, w) for k, w in sorted(kwargs.items())]
arg_lst.append(', '.join(pairs))
arg_str = ', '.join(arg_lst)
print('[%0.8fs] %s(%s) -> %r ' % (elapsed, name, arg_str, result))
return dec
obj = T1('test')
@obj
def t2():
l = [1, 2, 3]
ff = [x*x for x in l]
print(ff)
t2()