装饰器

前提知识:python函数(高阶函数)、闭包的概念

实质:一个函数,参数和返回值是另一个函数/方法

装饰器是在python2.4之后新加入的.


如何定义

自定义的装饰器

1
2
3
4
5
def mydecorator(function):
def _mydecorator(*args, **kw):
return function(*args, **kw)
return _mydecorator

测试代码运行时间的装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import time
def mydecorator(function):
def _mydecorator(*args, **kw):
start = time.time()
res = function(*args, **kw)
print time.time() - start
return res
return _mydecorator
foo = mydecorator(foo)
@mydecorator
def foo():
print 2**128
foo()

使用了@语法糖之后,看着就比较好了。装饰器实际上是一个闭包,装饰器完成了两个操作。

第一个装饰器中的操作

第二返回了一个函数


如何保存被装饰的函数的元数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
In [23]: def d(fun):
...: """
...: this is d
...: """
...: def _d():
...: fun()
...: return _d
...:
In [24]: @d
...: def e():
...: """
...: this ie e
...: """
...: print("hello e")
In [26]: e.__name__
Out[26]: '_d'
In [27]: e.__annotations__
Out[27]: {}

可以看到,被装饰的函数e的元属性改变了.


解决方法

1
2
3
4
5
6
7
8
9
10
In [29]: from functools import wraps
In [30]: def d(fun):
...: """
...: this is d
...: """
...: @wraps(fun)
...: def _d():
...: fun()
...: return _d

如何去掉装饰器的效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def mydecorator(function):
@wraps(function)
def _mydecorator(*args, **kw):
start = time.time()
res = function(*args, **kw)
print(time.time() - start)
return res
return _mydecorator
# foo = mydecorator(foo)
@mydecorator
def foo():
print(2**128)
foo()
orig_foo = foo.__wrapped__
orig_foo()

上面这个在py3.x才生效.


让装饰器带上参数

装饰器带上参数,需要在多加一层包裹的函数.

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env python
#coding: utf-8
def mydecorator(arg1, arg2):
def _mydecorator(function):
def __mydecorator(*args, **kw):
# you can check the args before the function
res = function(*args, **kw)
return __mydecorator
return _mydecorator

eg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from functools import wraps
import logging
def logged(level, name=None, message=None):
def decorate(func):
logging.basicConfig(level=level, format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
logname = name if name else func.__module__
log = logging.getLogger(logname)
logmsg = message if message else func.__name__
@wraps(func)
def wrapper(*args, **kargs):
log.log(level, logmsg)
return func(*args, **kargs)
return wrapper
return decorate
if __name__ == "__main__":
@logged(logging.CRITICAL, "CALL ADD")
def add(a, b):
return a + b
print add(1, 2)

可以调整参数的装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from functools import wraps, partial
import logging
#
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, message=None):
'''
Add logging to a function. level is the logging
level, name is the logger name, and message is the
log message. If name and message aren't specified,
they default to the function's module and name.
'''
def decorate(func):
logname = name if name else func.__module__
log = logging.getLogger(logname)
logmsg = message if message else func.__name__
@wraps(func)
def wrapper(*args, **kwargs):
log.log(level, logmsg)
return func(*args, **kwargs)
# Attach setter functions
@attach_wrapper(wrapper)
def set_level(newlevel):
nonlocal level
level = newlevel
@attach_wrapper(wrapper)
def set_message(newmsg):
nonlocal logmsg
logmsg = newmsg
return wrapper
return decorate
# Example use
@logged(logging.DEBUG)
def add(x, y):
return x + y
@logged(logging.CRITICAL, 'example')
def spam():
print('Spam!')

装饰器的应用场景

参数检查

缓存

常用于递归等

代理

###

坚持技术分享,您的支持将鼓励我继续创作!