装饰器
人要衣装,佛要金装,函数要装饰器。要了解装饰器,首先我们需要了解高阶函数,函数在python中作为一级类对象,可以被当作参数传递,也作为函数返回,简单来说高阶函数就是不是单一函数对象,里面函数嵌套函数,所以这么说python是支持函数式编程的,而装饰器首先它肯定是一个高阶函数,本质即是接受函数作为参数,并且返回一个函数,装饰器装饰了函数,可以在函数执行的前后做一些操作,外层函数的作用域的变量(但不是全局变量)可以被内部函数应用,这又是python闭包的概念,装饰器集合了许多python函数的概念,所以一直被称为最难掌握的内容。
理解装饰器
先简单的来一个例子:
1 | import time |
2 | def timeit(some_func): |
3 | def inner(*args,**kwargs): |
4 | start=time.time() |
5 | ret=some_func(*args,**kwargs) |
6 | spend_time=time.time()-start |
7 | return spend_time |
8 | return inner |
9 | |
10 | def sleep(x): |
11 | time.sleep(x) |
12 | |
13 | decorated=timeit(sleep) |
14 | decorated(3) |
15 | 3.00376296043396 |
结合闭包和函数的概念,整个函数应该很容易理解,执行的过程如:timeit(sleep) -> inner(3),我们将sleep函数作为参数传入timeit,timeit返回inner函数,根据闭包(函数+应用环境),在外层函数的应用环境被引入inner内层函数,所以很顺利的sleep函数与inner函数就建立了连接,在inner函数中的some_func即是sleep函数。所以说到这,装饰器也可以说是一个闭包。
更深一步,我们加入语法糖了尝试下:
1 | import time |
2 | def timeit(some_func): |
3 | def inner(*args,**kwargs): |
4 | start=time.time() |
5 | ret=some_func(*args,**kwargs) |
6 | spend_time=time.time()-start |
7 | return spend_time |
8 | return inner |
9 | |
10 |
|
11 | def sleep(x): |
12 | time.sleep(x) |
13 | |
14 | sleep(3) |
15 | 3.001178741455078 |
整个引用过程变得更加简单,而根据上一个函数我们可以得出,不带参数的装饰器可以理解做的是:sleep=timeit(sleep)
上面我们说是不带参数的装饰器,那么相反的有带参数的装饰器:
1 | import time |
2 | def timeit(offset=0): |
3 | def outer(some_func): |
4 | def inner(*args,**kwargs): |
5 | start=time.time() |
6 | ret=some_func(*args,**kwargs) |
7 | spend_time=time.time()-start+offset |
8 | return spend_time |
9 | return inner |
10 | return outer |
11 | |
12 | def sleep(x): |
13 | time.sleep(x) |
14 | |
15 | dec=timeit(1) |
16 | dec2=dec(sleep) |
17 | dec2(3) |
18 | 4.002138137817383 |
相信理解了不带参数的参数的装饰器,对于这个拆解的带参数的装饰器也是比较好理解的,执行过程:timeit(1) -> outer(sleep) -> inner(3),其实说到这,我是怎么理解装饰器就是多层次的闭包。
我们再用python语法糖来尝试下带参数的装饰器:
1 | import time |
2 | def timeit(offset=0): |
3 | def outer(some_func): |
4 | def inner(*args,**kwargs): |
5 | start=time.time() |
6 | ret=some_func(*args,**kwargs) |
7 | spend_time=time.time()-start+offset |
8 | return spend_time |
9 | return inner |
10 | return outer |
11 | |
12 |
|
13 | def sleep(x): |
14 | time.sleep(x) |
15 | |
16 | sleep(3) |
17 | 4.004631042480469 |
根据上边的语法糖的理解,带参数的装饰器的过程应该是:sleep=timeit(1)(sleep)
这里需要说下的是带参数的装饰器的语法糖,后边只允许带一个参数括号
。
还有一种用法就是多个装饰器的叠加,这里也试着写个例子说明下:
1 | def deco_1(func): |
2 | print('enter into deco_1') |
3 | def wrapper(a,b): |
4 | print('enter into deco_1_wrapper') |
5 | func(a,b) |
6 | return wrapper |
7 | |
8 | def deco_2(func): |
9 | print('enter into deco_2') |
10 | def wrapper(a,b): |
11 | print('enter into deco_2_wrapper') |
12 | func(a,b) |
13 | return wrapper |
14 | |
15 |
|
16 |
|
17 | def addFun(a,b): |
18 | print('result is %d' % (a+b)) |
19 | |
20 | addFun(1,2) |
21 | enter into deco_2 |
22 | enter into deco_1 |
23 | enter into deco_1_wrapper |
24 | enter into deco_2_wrapper |
25 | result is 3 |
从这个例子中,可以看出多层套接字是从下向上执行的:addFun(1,2)=deco_1(deco_2(addFun(1,2)))
类装饰器
使用类装饰器,还可以依靠类的内部的call方法,当使用@形式将装饰器附加到函数上时,就会调用此方法。
1 | |
2 | class foo(object): |
3 | def __init__(self,func): |
4 | self._func=func |
5 | def __call__(self): |
6 | print('class decorater running') |
7 | self._func() |
8 | print('class decorator stopping') |
9 | |
10 |
|
11 | def bar(): |
12 | print('bar') |
13 | |
14 | bar() |
15 | class decorater running |
16 | bar |
17 | class decorator stopping |
保留原函数的信息
1 | import time |
2 | def timeit(some_func): |
3 | def inner(*args,**kwargs): |
4 | start=time.time() |
5 | ret=some_func(*args,**kwargs) |
6 | spend_time=time.time()-start |
7 | return spend_time |
8 | return inner |
9 | |
10 |
|
11 | def sleep(x): |
12 | ''' sleep ''' |
13 | time.sleep(x) |
14 | |
15 | print(sleep.__name__,sleep.__doc__) |
16 | |
17 | inner None |
我们知道在整个生成器的执行过程,我们经过了sleep=timeit(sleep),所以在我们获得sleep的时候函数的内存指针应该指向的inner的函数,所以原本sleep的函数信息就全部没了,成了一个新函数,而为了避免这种情况,functools中wraps可以将原本的函数属性全部更新到新函数中。
1 | from functools import wraps |
2 | import time |
3 | def timeit(some_func): |
4 |
|
5 | def inner(*args,**kwargs): |
6 | start=time.time() |
7 | ret=some_func(*args,**kwargs) |
8 | spend_time=time.time()-start |
9 | return spend_time |
10 | return inner |
11 | |
12 |
|
13 | def sleep(x): |
14 | ''' sleep ''' |
15 | time.sleep(x) |
16 | |
17 | print(sleep.__name__,sleep.__doc__) |
18 | |
19 | sleep sleep |
简单的装饰器的应用
auth认证
1 | #!/usr/bin/env python |
2 | #-*- coding=utf8 -*- |
3 | |
4 | def checkpass(func): |
5 | def inner(): |
6 | if pass_database.get(user,None) == password: |
7 | ret=func() |
8 | return ret |
9 | else: |
10 | print('Password is not correct.') |
11 | return inner |
12 | |
13 | def checkuser(func): |
14 | def inner(): |
15 | if user in user_database: |
16 | ret=func() |
17 | return ret |
18 | else: |
19 | print('User is not present.') |
20 | return inner |
21 | |
22 |
|
23 |
|
24 | def index(): |
25 | print('Hello {}!'.format(user)) |
26 | |
27 | user_database=['jusene','jhon'] |
28 | pass_database={'jusene':'1234','jhon':'1234'} |
29 | |
30 | user=input('请输入user: ') |
31 | password=input('请输入pass: ') |
32 | index() |
33 | |
34 | ➜ ~ python test.py |
35 | 请输入user: jusene |
36 | 请输入pass: 1234 |
37 | Hello jusene! |
38 | ➜ ~ python test.py |
39 | 请输入user: j |
40 | 请输入pass: 1234 |
41 | User is not present. |
42 | ➜ ~ python test.py |
43 | 请输入user: j |
44 | 请输入pass: 21 |
45 | User is not present. |
46 | ➜ ~ python test.py |
47 | 请输入user: jusene |
48 | 请输入pass: 222 |
49 | Password is not correct. |
cache缓存
1 | #!/usr/bin/env python |
2 | #-*- coding=utf8 -*- |
3 | |
4 | from functools import wraps |
5 | |
6 | def cache(instance): |
7 | def dec(fn): |
8 |
|
9 | def wrap(*args,**kwargs): |
10 | pos=','.join([str(x) for x in args]) |
11 | kw=','.join(['{}={}'.format(k,v) for k,v in sorted(kwargs.items())]) |
12 | key='{}::{}::{}'.format(fn.__name__,pos,kw) |
13 | print(key) |
14 | ret=instance.get(key) |
15 | if ret is not None: |
16 | return ret |
17 | ret=fn(*args,**kwargs) |
18 | instance.set(key,ret) |
19 | return ret |
20 | return wrap |
21 | return dec |
22 | |
23 | class DictCache: |
24 | def __init__(self): |
25 | self.cache=dict() |
26 | def get(self,key): |
27 | return self.cache.get(key) |
28 | def set(self,key,value): |
29 | self.cache[key]=value |
30 | def __str__(self): |
31 | return str(self.cache) |
32 | def __repr__(self): |
33 | return str(self.cache) |
34 | |
35 | cache_instance=DictCache() |
36 | |
37 | import time |
38 |
|
39 | def long_time_fun(x): |
40 | time.sleep(x) |
41 | return x |
42 | |
43 | 3) long_time_fun( |
44 | long_time_fun::3:: #需要等3秒 |
45 | 3 |
46 | 3) long_time_fun( |
47 | long_time_fun::3:: #秒出 |
48 | 3 |
49 | |
50 | 3) long_time_fun(x= |
51 | long_time_fun::::x=3 |
52 | 3 |
53 | 3) long_time_fun(x= |
54 | long_time_fun::::x=3 |
55 | 3 |