装饰器是什么
“装饰器”(Decorator)可以让其他函数在不需要做任何代码变动的前提下增加额外功能。简单的说,装饰器的作用就是为已经存在的函数或对象添加额外的功能。本质上,decorator是一个返回函数的高阶函数。具体使用场景:插入日志、性能测试、事务处理、缓存、权限校验等场景。
之前通过实际代码学习和装饰器的具体用途。代码记录如下:
普通装饰器
python提供了可变参数*args和关键字参数**kwargs,有了这两个参数,装饰器可以用于带任意参数的目标函数。1
2
3
4
5
6
7
8
9
10
11
12
13import functools
# 普通装饰器,打印程序执行日志
def debug(func):
def wrapper(*args,**kwargs):
print("[debug]: enter {}()".format(func.__name__))
return func(*args,**kwargs)
return wrapper
def say_goodbye(name):
print('goodbye,%s' % name)
say_goodbye(name='Fan')
带参数的装饰器
如果前文的装饰器需要完成的功能不仅仅是能在进入某个函数后打出log信息,而且还需指定log的级别,那么装饰器代码如下。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import functools
def log(level):
def debug(func):
def wrapper(*args,**kwargs):
print("[debug] %s %s" % (level,func.__name__))
return func(*args,**kwargs)
return wrapper
return debug
def say_goodbye(name):
print('goodbye,%s' % name)
say_goodbye(name='Fan')
实例
通过装饰器测试程序执行时间。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import functools,time
def metic(func):
def wrapper(*args,**kw):
start=time.time()
result=func(*args,**kw)
end=time.time()
print("%s excute time: %s ms" % (func.__name__,end-start))
return result
return wrapper
def fast(x,y):
time.sleep(0.1)
return x+y
def slow(x,y,z):
time.sleep(0.2)
return x*y*z
类装饰器
装饰器函数接受一个callable对象作为参数,然后返回一个callable对象。在python中一般callable对象都是函数,但也有例外。只要某个对象重载了call()方法,那么这个对象就是callable的。类似call这样前后都带下划线的方法在Python中被称为内置方法,重载这些方法一般会改变对象的内部行为。
如下列代码所示,类的构造函数init()接受一个函数,然后重载call()并返回一个函数,也可以达到装饰器函数的效果。1
2
3
4
5
6
7
8
9
10
11
12
13import functools
class logging(object):
"""docstring for Test"""
def __init__(self,func):
self.func=func
def __call__(self,*args,**kw):
print("[DEBUG]:enter function {func}()".format(func=self.func.__name__))
return self.func(*args,**kw)
def test_logging(something):
print("test_logging %s !" % something)
test_logging('nothing')
带参数的类装饰器
如果需要通过类形式实现带参数的装饰器,那么在构造函数里接受的就不是一个函数,而是传入的参数。通过类把这些参数保存起来,然后再重载call方法是就需要接受一个函数并返回一个函数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import functools
class Debug(object):
"""docstring for Debug"""
def __init__(self,level='INFO'):
super(Debug, self).__init__()
self.level = level
def __call__(self,func):
def wrapper(*args,**kw):
print("Debug:[{level}] enter function {func}()".format(level=self.level,func=func.__name__))
func(*args,**kw)
return wrapper
def debug_test(func,name):
print("Hello,%s %s" % (func,name))
debug_test(func='Fan',name='Tongxue')