引入装饰器的原因主要是为了在不修改原有函数代码的情况下,增加额外功能,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
实现的原理其实就是又写了一个日志函数(例如log函数),将功能函数(func)作为参数传递进去,并把功能函数(func)的调用写在log函数中,除此之外,进行一些额外的操作. 考虑以下代码:
def log(func):
def wrapper(*args, **kwargs):
print("run function%s" % func.__name__)
return func(*args, **kwargs)
return wrapper
@log
def add(a, b):
return a + b
@log
def sub(a, b):
return a - b
print(add(1, 2))
print(sub(1, 2))
我们原有的函数有两个,add和sum分别完成加法和减法的操作,如果我们每次操作的需要记录到日志,你应该怎么做?当然你可以在每个函数中添加一个print,类似下面:
def add(a, b):
print("run function add")
return a + b
但如果大型项目有100000个函数,每个函数都极其复杂,又有不同人维护,你要手动添加100000个print吗?老板基于代码重用的思想就把你炒了。
当然你也可以写一个log函数,然后每个原始功能函数在最后去调用这个log函数,这样实现的代码重用,例如下面:
def add(a, b):
log("add")
return a + b
这样做也会有一些问题,比如这个功能函数是别人维护的,而你的log函数又足够复杂,他并不知道需要给你传递什么参数,每次还要来问你,毕竟你写的函数参数只有你和天知道是怎么回事。除此之外,如何传递功能函数的名字、属性等信息,说出来你可能不信,我刚查了一波,在函数内部获得函数名是sys._getframe().f_code.co_name 这个玩意,你愿意去记吗?还有其他属性呢?我要是写功能函数,大概不把你锤死。最后一点就是pythonic,简洁高效,功能函数的编写者只需要知道日志函数的函数名即可,他不需要知道日志函数的功能和需求,毕竟大家都很忙。
最后,如果需要在log中传递参数,可以这样写:
def log(level):
def dec(func):
def wrapper(*args, **kwargs):
print("run function%s, level%d" % (func.__name__, level))
return func(*args, **kwargs)
return wrapper
return dec
@log(level=1)
def add(a, b):
return a + b
@log(level=2)
def sub(a, b):
return a - b
print(add(1, 2))
print(sub(1, 2))