装饰器
装饰模式有很多经典的使用场景,例如插入日志、性能测试、事务处理等等,有了装饰器,就可以提取大量函数中与本身功能无关的类似代码,从而达到代码重用的目的。
装饰器引入
下面就简单举个例子:
一天,A程序员接到一个登入的需求,写了一个方法。
def login():
print('登入')
login()
# 输出
登入
1
2
3
4
5
6
7
8
9
突然,产品经理想加入一个登入事件。于是A程序员对方法进行了修改。
def login():
print('事件记录')
print('登入')
login()
# 输出
事件记录
登入
1
2
3
4
5
6
7
8
9
10
11
然后,A的老大看到了说,你这样违反了开闭原则,好好思考一下如何改进。后面A就改进,通过闭包的方式,定义了一个event函数。
# 定义一个event函数,记录事件
def event(func):
def inner():
print('事件记录')
func()
return inner
def login():
print('登入')
login = event(login) # 这个代码意思是,把定义的login函数作为变量传递给event作为参数。event函数的返回值在赋值login变量
login() # 调用login变量的方法
# 输出
事件记录
登入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
这里有个比较难理解的地方就是,def定义了一个login函数,实际上它是一个login变量指向def定义的login函数的地址值。
eg:
def login():
print('登入')
print(login) # <function login at 0x000001D8D0543E18>
a = login # 将login函数的内存地址指向给a变量
a() # 调用a函数,实际上调用的是a指向的地址值的方法
# 输出
登入
1
2
3
4
5
6
7
8
9
10
这个例子就说明login是一个变量,这个变量指向login函数地址值。
理解了这个例子,那么login = event(login)就比较好解释了。
login = event(login)
等号左边的login是event()的返回值,是经过inner变量赋值,inner变量指向的是inner函数的地址值。
等号右边的login变量是指向login函数的地址值。作为参数传递给event函数。实际上是在inner函数中调用。
login()
调用login函数,因为login变量经过inner变量赋值,所以这时的login变量指向的是inner函数的地址值。
也就是说,login()调用,实际上调用的是inner函数,inner函数执行的顺序是:
print('事件记录')
func()
func参数是由event函数传入,也就是login = event(login)式子等号右边的login,这个login变量指向的是def定义login()。
所以输出的结果是:
事件记录
登入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
装饰器语法糖
在Python中,可以使用@语法糖来精简装饰器的代码:
使用了@语法糖后,我们就不需要额外代码来给login重新赋值了。
eg:
def event(func):
def inner():
print('事件记录')
func()
return inner
@event # 实际上做了 login = event(login)操作
def login():
print('登入')
login()
# 输出
事件记录
登入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
这里login()的@event,其实本质就是login = event(login),@做了一步赋值操作。当认清了这一点后,后面看带参数的装饰器就简单了。
被装饰的函数带参数
我们只带login()作为登入函数,实际上是有用户名和密码的,这个时候login()需要2个参数,那么怎么处理呢。
eg:
def event(func):
def inner(username, password):
print('事件记录')
func(username, password)
return inner
@event
def login(username, password):
print('username is %s password is %s,登入成功' %(username,password))
login('amy','123456')
# 输出
事件记录
username is amy password is 123456
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
如果这时候需要加上一个注册的方法,注册需要3个参数,怎么使用呢?这个时候需要使用可变参数*args, **kwargs。
eg:
def event(func):
def inner(*args, **kwargs):
print('事件记录')
func(*args, **kwargs)
return inner
@event
def login(username, password):
print('username is %s , password is %s,登入成功' % (username, password))
@event
def register(username, password, email):
print('username is %s , password is %s , email is %s,注册成功' % (username, password, email))
login('amy','123456')
# 输出
事件记录
username is amy , password is 123456 , 登入成功
register('anne', '654321' , 'abc@126.com')
# 输出
事件记录
username is amy , password is 123456 , email is abc@126.com , 注册成功
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
被修饰的函数带返回值
现在登入和注册成功后,需要有返回值,那么怎么处理,请看下面例子:
def event(func):
def inner(*args, **kwargs):
print('事件记录')
ret = func(*args, **kwargs)
return ret
return inner
@event
def login(username, password):
print('username is %s , password is %s' % (username, password))
return '登入成功'
@event
def register(username, password, email):
print('username is %s , password is %s , email is %s' % (username, password, email))
return '注册成功'
ret = login('amy','123456')
print(ret) # 输出 登入成功
ret = register('anne', '654321' , 'abc@126.com')
print(ret) # 输出 注册成功
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
如果函数没有返回值,怎么处理,函数没有返回值,其实返回的是None。上面的装饰器是比较通用的装饰器。
带参数的装饰器
eg:
def event(debug=True):
if (debug):
def deco(func):
def inner(*args, **kwargs):
print('事件记录')
return func(*args, **kwargs)
return inner
else:
def deco(func):
return func
return deco
@event() # 使用默认参数
def login(username, password):
print('username is %s , password is %s' % (username, password))
return '登入成功'
@event(debug=False) # debug参数设置成False,也可以直接写False
def register(username, password, email):
print('username is %s , password is %s , email is %s' % (username, password, email))
return '注册成功'
login('amy', '123456')
# 输出
事件记录
username is amy , password is 123456
register('anne', '654321', 'abc@126.com')
# 输出
username is anne , password is 654321 , email is abc@126.com
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
@event加上参数,目的是可以灵活的控制函数的输出,如是否打开调试信息等。
如果@event加上参数,在login()函数定义之前就开始执行。我们可以看一下装饰器的执行顺序。
eg:
def event(debug=True):
print('event函数调用')
if (debug):
def deco(func):
print('deco函数调用')
def inner(*args, **kwargs):
print('inner函数调用')
# print('事件记录')
return func(*args, **kwargs)
return inner
else:
def deco(func):
print('deco函数调用')
return func
return deco
@event()
def login(username, password):
print('login函数调用')
# print('username is %s , password is %s' % (username, password))
return '登入成功'
# 在未调用函数时,运行,打印输出:
event函数调用
deco函数调用
login()
# 调用函数后,运行,输出:
event函数调用
deco函数调用
inner函数调用
login函数调用
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
也就是说,定义的event方法和deco方法在未调用时Python解释器就已经开始执行了。
执行步骤:
1.定义event函数
2.调用event函数,执行打印event函数调用语句,返回deco函数的引用
3.使用@event,执行打印deco函数调用语句,返回inner函数的引用
4.使用func进行装饰
5.调用login函数
6.执行inner函数调用语句
7.执行login函数调用语句
---------------------
【转载】
作者:张行之
来源:CSDN
原文:https://blog.csdn.net/qq_33689414/article/details/78295628
版权声明:本文为博主原创文章,转载请附上博文链接!
|
|