我只是技术的搬运工
简介:对Python装饰器的一点个人理解
在这里用一个简单的例子分析Python的装饰器的过程:
import functools
def log(func=None, logger=None):
if func is not None:
@functools.wraps(func)
def wrapper(*args, **kwargs):
if logger:
logger.info(func.func_name + " will be called. (at A)")
else:
print(func.func_name + " will be called. (at a)")
return func(*args, **kwargs)
return wrapper
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if logger:
logger.info(func.func_name + " will be called. (at B)")
else:
print(func.func_name + " will be called. (at b)")
return func(*args, **kwargs)
return wrapper
return decorator
@log
def funcA(*args, **kwargs):
print "funcA was called."
@log()
def funcB(*args, **kwargs):
print "funcB was called."
funcA()
funcB()
#################output######################################
funcA will be called. (at a)
funcA was called.
funcB will be called. (at b)
funcB was called.
在分析这个例子之前,我们先来看看Python的装饰器的过程(个人理解):
@decorator
def func_name(*args, **kwargs):
pass
在我们调用func_name()
的时候,我们的调用逻辑过程,我认为是这样的:decorator(func_name)(*args, **kwargs)
。
现在我们回到上面的例子:
funcA() => log(funcA)(*args, **kwargs) => wrapper(*args, **kwargs)
funcB() => log()(funcB)(*args, **kwargs) = decorator(funcB)(*args, **kwargs) => wrapper(*args, **kwargs)
在最后都是调用wrapper,只是带参数的装饰器多调用了一层而已,为了得到返回值,我们一般是在wrapper调用func并返回func的执行结果。
我们搞清楚了这个过程之后,其实装饰器不止限定函数,装饰器也不止可以装饰函数了。
class log(object):
def __init__(self, func=None):
self.func = func
def __call__(self,*args, **kwargs):
print(self.func.func_name + " called. at log")
return self.func(*args, **kwargs)
class logb(object):
def __init__(self):
pass
def __call__(self, func):
def wrapper(*args, **kwargs):
print(func.func_name + " called. at logb")
return func(*args, **kwargs)
return wrapper
@log
def funcA():
print "funcA called"
@logb()
def funcB():
print "funcB called"
funcA()
funcB()
上面的例子的调用过程:
funcA() => log(funcA)(*args, **kwargs) => log(funcA).__call__(*args, **kwargs)
funcB() => logb()(funcB)(*args, **kwargs) => logb().__call__(funcB)(*args, *kwargs) => wrapper(*args, **kwargs)
只要理解了这个过程装饰器就很简单了。其实函数就是带有__call__的对象(个人理解),因此装饰器不仅可以装饰函数,还可以装饰类,不过个人感觉没什么作用,当然装饰器可以装饰类方法不过第一个参数必须得是self或是cls。
注意:链式装饰器的装饰过程是由下往上装饰的。
@decorator_a
@decorator_b
def func(*args):
pass
装饰过程是由下往上了,因此先是decorator_b(func)(*args)
,然后decorator_a(decorator_b(func))(*args)
。