转自小马哥:
闭包和装饰器充分体现了Python语法糖的优雅感觉。
在本文中,我们的实验要完成两个工作,一个是加法,一个是累计调用加法的次数,最普通的Python程序可以这么写:
1 def validate(a, b):
2 if ((isinstance(a, int) or isinstance(a, float))
3 and (isinstance(b, int) or isinstance(b, float))):
4 return True
5 return False
6
7
8 def add(a, b):
9 if validate(a, b):
10 return a + b
11 return "Invalid."
12
13
14 count = 0
15
16
17 def counter(func):
18 global count
19 count = count + 1
20 return (count, func)
21
22 print(counter(add(1, 2)))
23 print(counter(add(1, 3)))
24 print(counter(add('1', 4)))
25 print(counter(add(1, 5)))
26 print(counter(add(1, 6)))
结果:
>> long@happytime:~/development/closure$ python3 test.py
>> (1, 3)
>> (2, 4)
>> (3, 'Invalid.')
>> (4, 6)
>> (5, 7)
虽然可以正确显示结果,但是有如下几个不满意的地方:
- 全局变量的使用首先是不提倡的;
- 每次调用add都需要手动调用counter;
- add函数和validate函数耦合在一起,如果以后需要判断更多条件,会产生很多层的if-else的嵌套。
用闭包解决问题1:
1 def validate(a, b):
2 if ((isinstance(a, int) or isinstance(a, float))
3 and (isinstance(b, int) or isinstance(b, float))):
4 return True
5 return False
6
7
8 def add(a, b):
9 if validate(a, b):
10 return a + b
11 return "Invalid."
12
13
14 def counter():
15 ct = 0
16
17 def do(func):
18 nonlocal ct
19 ct = ct + 1
20 return (ct, func)
21 return do
22
23
24 ct = counter()
25 print(ct(add(1, 2)))
26 print(ct(add(1, 3)))
27 print(ct(add('1', 4)))
28 print(ct(add(1, 5)))
29 print(ct(add(1, 6)))
结果:
>> long@happytime:~/development/closure$ python3 test1.py
>> (1, 3)
>> (2, 4)
>> (3, 'Invalid.')
>> (4, 6)
>> (5, 7)
用装饰器进一步解决问题2:
1 def validate(a, b):
2 if ((isinstance(a, int) or isinstance(a, float))
3 and (isinstance(b, int) or isinstance(b, float))):
4 return True
5 return False
6
7
8 def counter(func):
9 ct = 0
10
11 def count(a, b):
12 nonlocal ct
13 ct = ct + 1
14 return (ct, func(a, b))
15 return count
16
17
18 @counter
19 def add(a, b):
20 if validate(a, b):
21 return a + b
22 return "Invalid."
23
24
25 print(add(1, 2))
26 print(add(1, 3))
27 print(add('1', 4))
28 print(add(1, 5))
29 print(add(1, 6))
结果:
>> long@happytime:~/development/closure$ python3 test2.py
>> (1, 3)
>> (2, 4)
>> (3, 'Invalid.')
>> (4, 6)
>> (5, 7)
用装饰器进一步解决问题3:
1 def validate(func):
2 def do(a, b):
3 if ((isinstance(a, int) or isinstance(a, float))
4 and (isinstance(b, int) or isinstance(b, float))):
5 return func(a, b)
6 return "Invalid."
7 return do
8
9
10 def counter(func):
11 ct = 0
12
13 def count(a, b):
14 nonlocal ct
15 ct = ct + 1
16 return (ct, func(a, b))
17 return count
18
19
20 @counter
21 @validate
22 def add(a, b):
23 return a + b
24
25
26 print(add(1, 2))
27 print(add(1, 3))
28 print(add('1', 4))
29 print(add(1, 5))
30 print(add(1, 6))
结果:
>> long@happytime:~/development/closure$ python3 test3.py
>> (1, 3)
>> (2, 4)
>> (3, 'Invalid.')
>> (4, 6)
>> (5, 7)
运用装饰器,可以在执行add的同时做很多事情,但耦合度很低,需要就加上装饰器,不需要就去掉。不过需要注意的是,多个装饰器的调用顺序是从下到上。所以@validate在@counter的下面。