装饰器是Python中的魔法所在,下边让我们来一探究竟。
话不多说, 上代码。
前菜
from functools import wraps
import time
def timethis(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print("The running time is ", end-start)
return result
return wrapper
@timethis
def add(x, y):
res = []
for i in range(100000):
res.append(x+y)
return res
if __name__ == "__main__":
a = add(5, 10)
print(a)
装饰器的运作原理其实很简单,添加@timethis会发生什么?下面就一一道来。其实就是简单的把add函数包裹了一下。
func = timethis(func), timethis 里边的@wraps(func)一定要加,不然访问不到函数的元信息。 比如你想使用原始的函数而不是包裹后函数。你可以使用__wrapped__属性。
ori_add = add.wrapped
ori_add(5, 10)
如果不加@wraps(func),程序运行就会报错。
前菜可还可口?几下来主菜要来了
主菜 带参数的装饰器
from functools import wraps
import logging
logging.basicConfig(level=logging.DEBUG)
def logged(level, name=None, msg=None):
def decorator(func):
logname = name
log = logging.getLogger(logname)
logmsg = msg
@wraps(func)
def wrapper(*args, **kwargs):
log.log(level, logmsg)
return func(*args, **kwargs)
return wrapper
return decorator
@logged(logging.DEBUG, "add", "This is an add function")
def add(x, y):
return x + y
if __name__ == "__main__":
add(3, 5)
添加logged装饰器,其实和下边的代码时等价的。
new_add = logged(logging.DEBUG, “add”, “This is an add function”)(func)
这样就又回到前菜了,可以体会一下, 其实所有的装饰器都时这样工作的,掌握本质才是真正学会一个东西。
大餐来了 给wrapper添加属性
from functools import wraps, partial
import logging
logging.basicConfig(level=logging.DEBUG)
def attach_wrapper(obj, func=None):
if func is None:
return partial(attach_wrapper, obj)
setattr(obj, func.__name__, func)
return func
def logged(level, name=None, msg=None):
def decorator(func):
logname = name
log = logging.getLogger(logname)
logmsg = msg
@wraps(func)
def wrapper(*args, **kwargs):
log.log(level, logmsg)
return func(*args, **kwargs)
@attach_wrapper(wrapper)
def set_level(newlevel):
nonlocal level
level = newlevel
@attach_wrapper(wrapper)
def set_msg(newmsg):
nonlocal logmsg
logmsg = newmsg
return wrapper
return decorator
@logged(logging.DEBUG, "add", "This is an add function")
def add(x, y):
return x + y
if __name__ == "__main__":
add(3, 5)
add.set_level(logging.CRITICAL)
add(3, 4)
add.set_msg("This is testing attaching attr")
add(5, 6)
上面这段代码核心时attach_wrapper()这个函数,这个函数主要作用其实就是给wrapper对象添加两个属性set_level和set_msg.这样我们就能在包裹后的add函数中访问这两个属性了。
最后的咖啡
装饰器时python语言的魔法,非常非常的灵活,很容易扩展,并且没有耦合。灵活运用装饰器,可以让你的代码更有魔力。不要重复自己,尝试新的挑战,这样才能在编码世界走的更远。