python装饰器

1. 装饰器的原型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import time
def showtime(func):
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print('spend is {}'.format(end_time - start_time))

return wrapper

def foo():
print('foo..')
time.sleep(3)

foo = showtime(foo)
foo()

foo函数进行增强;

2. 不带参数的装饰器:(装饰器,被装饰函数都不带参数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import time
def showtime(func):
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print('spend is {}'.format(end_time - start_time))

return wrapper

@showtime #foo = showtime(foo)
def foo():
print('foo..')
time.sleep(3)

@showtime #doo = showtime(doo)
def doo():
print('doo..')
time.sleep(2)
if __name__ == '__main__':
foo()
doo()

3. 带参数的被装饰的函数

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
import time
def showtime(func):
def wrapper(a, b):
start_time = time.time()
func(a,b)
end_time = time.time()
print('spend is {}'.format(end_time - start_time))

return wrapper

@showtime #add = showtime(add)
def add(a, b):
print(a+b)
time.sleep(1)

@showtime #sub = showtime(sub)
def sub(a,b):
print(a-b)
time.sleep(1)
if __name__ == '__main__':
add(5,4)
sub(3,2)
'''
9
spend is 1.0008258819580078
1
spend is 1.0002799034118652

'''

4. 带参数的装饰器(装饰函数)

实际是对原有装饰器的一个函数的封装,并返回一个装饰器(一个含有参数的闭包函数),
当使用@time_logger(3)调用的时候,Python能发现这一层封装,并将参数传递到装饰器的环境去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import time
def time_logger(flag = 0):
def showtime(func):
def wrapper(a, b):
start_time = time.time()
func(a,b)
end_time = time.time()
print('spend is {}'.format(end_time - start_time))

if flag:
print('将此操作保留至日志')

return wrapper

return showtime

@time_logger(2) #得到闭包函数showtime,add = showtime(add)
def add(a, b):
print(a+b)
time.sleep(1)
if __name__ == '__main__':
add(3,4)

多加一层闭包,把参数传进去

5. 类装饰器:一般依靠类内部的call方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import time
class Foo(object):
def __init__(self, func):
self._func = func

def __call__(self):
start_time = time.time()
self._func()
end_time = time.time()
print('spend is {}'.format(end_time - start_time))

@Foo #bar = Foo(bar)
def bar():
print('bar..')
time.sleep(2)
if __name__ == '__main__':
bar()
'''
bar..
spend is 2.000215768814087
'''

6. 使用装饰器的缺点

1.位置错误的代码 不能在装饰器之外添加逻辑功能
2.不能装饰@staticmethod 或者 @classmethod已经装饰过的方法
3.装饰器会对原函数的元信息进行更改,比如函数的docstring,name,参数列表

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
import time
def showtime(func):
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print('spend is {}'.format(end_time - start_time))

return wrapper

@showtime #foo = showtime(foo)
def foo():
print('foo..')
time.sleep(3)

def doo():
print('doo..')
time.sleep(2)
if __name__ == '__main__':
print(foo.__name__)
print(doo.__name__)
'''
wrapper
doo
'''

由此可以看出,装饰器会对原函数的元信息进行更改,可以使用wraps,进行原函数信息的添加

注解:wraps本身也是一个装饰器,他能把函数的元信息拷贝到装饰器函数中使得装饰器函数与原函数有一样的元信息

以下是一个wraps的例子:

1
#6.4. 使用wraps把被装饰的函数信息绑定进装饰器
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
import time
from functools import wraps
def showtime(func):

@wraps(func)
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print('spend is {}'.format(end_time - start_time))

return wrapper

@showtime #foo = showtime(foo)
def foo():
print('foo..')
time.sleep(3)

def doo():
print('doo..')
time.sleep(2)
if __name__ == '__main__':
print(foo.__name__)
print(doo.__name__)
'''
foo
doo
'''

常用的内置装饰器

  1. staticmethod: 类似实现了静态方法 注入以后,可以直接 : 类名.方法

  2. property: 经过property装饰过的函数 不再是一个函数,而是一个property, 类似实现get,set方法

1
2
3
4
5
6
7
@property
def width(self):
return self.__width

@width.setter
def width(self, newWidth):
self.__width = newWidth
  1. classmethod: 与staticmethod很相似,貌似就只有这一点区别:
    第一个参数需要是表示自身类的 cls 参数,
    可以来调用类的属性,类的方法,实例化对象等。
    

详解Python的装饰器

理解Python装饰器(Decorator)

python 闭包,装饰器