本节内容
1、概述
2、装饰器定义
3、装饰器定义
4、带参数的生成器
一、概述
我们之前介绍了大幅片的内容,感觉跟装饰器半毛钱关系都没有,其实不然,我们分别详细阐述了高阶函数和内置函数,下面我们就来讲讲什么是真正的装饰器。
二、装饰器定义
首先装饰器实现的条件:高阶函数+嵌套函数 =》装饰器
1、定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
import time
#定义内置函数 def timmer(func): #timmer(test1) func=test1
def deco():
start_time = time.time()
func() #run test1()
stop_time = time.time()
print ( "the func run time is %s" % (stop_time - start_time))
return deco
#装饰test1函数 @timmer # 相当于test1 = timmer(test1)
def test1():
time.sleep( 3 )
print ( "in the test1" )
#直接执行test1函数 test1() #输出 in the test1
the func run time is 3.0002999305725098
|
执行步骤:
- 执行timmer函数,timmer(test1) 返回值赋值给test1变量,即test1=timmer(test1)
- 此时的test1的值是执行timmer函数返回值deco,即test1=deco
- 所以执行test1,其实就是执行的是deco函数,test1()其实就是执行deco函数。
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
|
import time
def timmer(func): #timmer(test1) func=test1
def deco():
start_time = time.time()
func() #run test1()
stop_time = time.time()
print ( "the func run time is %s" % (stop_time - start_time))
return deco
@timmer def test2(name,age):
print ( "name:%s,age:%s" % (name,age))
test2() #输出 Traceback (most recent call last): File "D:/PycharmProjects/pyhomework/day4/装饰器/装饰器高潮.py" , line 23 , in <module>
test2()
File "D:/PycharmProjects/pyhomework/day4/装饰器/装饰器高潮.py" , line 8 , in deco
func() #run test1()
TypeError: test2() missing 2 required positional arguments: 'name' and 'age' #缺少传入name和age参数
|
很显然是错误的,错误的。因为这边执行的test2函数其实就是执行的deco函数,deco函数体内的func()其实就是执行test2函数,但是,test2需要传入name和age两个参数,所以报错。那怎么解决呢?我们只能传入参数了,但是你又不能确定传入几个参数,所以我们只能用非固定参数传参。代码如下:
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
|
import time
def timmer(func): #timmer(test1) func=test1
def deco( * args, * * kwargs): #传入非固定参数
start_time = time.time()
func( * args, * * kwargs) #传入非固定参数
stop_time = time.time()
print ( "the func run time is %s" % (stop_time - start_time))
return deco
#不带参数 @timmer # 相当于test1 = timmer(test1)
def test1():
time.sleep( 3 )
print ( "in the test1" )
#带参数 @timmer def test2(name,age):
print ( "name:%s,age:%s" % (name,age))
#调用 test1() test2( "zhangqigao" , 22 )
#输出 #test1 in the test1
the func run time is 3.0010883808135986
#test2 name:zhangqigao,age: 22
the func run time is 0.0 #test2
|
三、执行函数有返回值
上面的例子,被调用的函数都没有返回值,那如果,我们被调函数有返回值,该如何做呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
def timmer(func): #timmer(test1) func=test1
def deco( * args, * * kwargs):
res = func( * args, * * kwargs) #这边传入函数结果赋给res
return res # 返回res
return deco
@timmer def test1(): # test1 = timmer(test1)
print ( "in the test1" )
return "from the test1" #执行函数test1有返回值
res = test1()
print (res)
#输出 in the test1
from the test1
|
通过上面的例子,可以看出,其实就是在内置函数中把传入参数的执行结果赋给res,然后再返回res变量。
四、带参数的装饰器
之前我们的装饰器都是没有带参数的,其实我们已经能解决90%的问题了,但是如果说有一种情况:就是在你访问不通页面时,你用的验证的方式来源不同,这时你该怎么办?
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
|
#本地验证 user,passwd = "zhangqigao" , "abc123"
def auth(auth_type): #传递装饰器的参数
print ( "auth func:" ,auth_type)
def outer_wrapper(func): # 将被装饰的函数作为参数传递进来
def wrapper( * args, * * kwargs): #将被装饰函数的参数传递进来
print ( "wrapper func args:" , * args, * * kwargs)
username = input ( "Username:" ).strip()
password = input ( "Password:" ).strip()
if auth_type = = "local" :
if user = = username and passwd = = password:
print ( "\033[32mUser has passed authentication\033[0m" )
res = func( * args, * * kwargs)
print ( "--after authentication" )
return res
else :
exit( "Invalid username or password" )
elif auth_type = = "ldap" :
pass
return wrapper
return outer_wrapper
def index():
print ( "welcome to index page" )
@auth (auth_type = "local" ) #带参数装饰器
def home():
print ( "welcome to home page" )
return "from home"
@auth (auth_type = "ldap" ) #带参数装饰器
def bbs():
print ( "welcome to bbs page" )
index() home() bbs() |
重上面的例子可以看出,执行步骤:
- outer_wrapper = auth(auth_type="local")
- home = outer_wrapper(home)
- home()
所以这个函数的作用分别是:
- auth(auth_type) 传递装饰器的参数
- outer_wrapper(func) 把函数当做实参传递进来
- wrapper(*args,**kwargs) 真正执行装饰的函数