在学习Python装饰器之前,先来看一下闭包(closure)。Python一切皆对象,函数这一语法结构也是一个对象。在函数对象中,我们像使用一个普通对象一样使用函数对象,比如更改函数对象的名字,或者将函数对象作为参数进行传递。闭包是一种组织代码的结构,提高了代码的重复实用性。在A函数中定义一个B函数,调用A函数的时候返回B函数,B函数对A函数作用域里变量的引用,B函数就叫做闭包。
来看一个闭包的例子:
1 def test(number):
2 #在函数内部再定义一个函数,这个函数就是闭包
3 def test_in(number_in):
4 print('in test_in,number_in is %d'%number_in)
5 return number+number_in
6 #返回闭包的结果
7 return test_in
8 result = test(20) #将实参20传递给形参number
9 print (result(100)) #将实参100传递给形参number_in
运行结果:
in test_in,number_in is 100
120
再来看一个引用外部函数变量的例子:
1 def counter(start=0):
2 count = [start]
3 def incr():
4 count[0] += 1 #内部函数引用外部函数变量
5 return count[0]
6 return incr
7 c = counter(5)
8 print (c())
9 print (c())
运行结果:
6
7
下面来看一下装饰器(decorator)。装饰器 可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。使用装饰器可以很好的实现开放封闭原则。先来一段装饰器的代码:
1 from time import ctime,sleep
2 def decorator_func(func):
3 def wrapper():
4 print('%s called at %s'%(func.__name__,ctime()))
5 func()
6 return wrapper
7 @decorator_func
8 def foo():
9 print ('I am foo')
10 foo()
11 sleep(2)
12 foo()
运行结果
foo called at Sun Nov 6 21:51:00 2016
I am foo
foo called at Sun Nov 6 21:51:02 2016
I am foo
上面这段代码该如何理解呢?在函数foo()前加入@decorator_func,在foo()执行时的相当于先执行了函数foo=decorator_func(foo),foo先作为参数赋值给func,foo接收指向decorator_func返回的wrapper;然后调用foo(),即相当于调用wrapper();内部函数wrapper被引用,外部函数func变量(自由变量)并没有被释放,即func里保存的是原foo函数对象。
也可以在被装饰的函数中加入参数,代码如下:
1 from time import ctime,sleep
2 def decorator_func(func):
3 def wrapper(a, b): #向装饰器中传入参数
4 print('%s called at %s'%(func.__name__,ctime()))
5 print (a, b)
6 func(a ,b)
7 return wrapper
8 @decorator_func
9 def foo(a, b):
10 print (a+b)
11 foo(1,2)
12 sleep(2)
13 foo(3,4)
运行结果:
foo called at Sun Nov 6 22:01:20 2016
1 2
3
foo called at Sun Nov 6 22:01:22 2016
3 4
7
如果传入的是不定长参数:
1 from time import ctime,sleep
2 def decorator_func(func):
3 def wrapper(*args, **kwargs): #向装饰器中传入不定长参数
4 print('%s called at %s'%(func.__name__,ctime()))
5 func(*args, **kwargs)
6 return wrapper
7 @decorator_func
8 def foo(a, b, c):
9 print (a+b+c)
10 foo(1,2,3)
11 sleep(2)
12 foo(4,5,6)
运行结果:
foo called at Sun Nov 6 22:07:18 2016
6
foo called at Sun Nov 6 22:07:20 2016
15
考虑到更通用的情况,我们可以在装饰器中加入return,先看下不加return的效果:
1 from time import ctime,sleep
2 def decorator_func(func):
3 def wrapper(*args, **kwargs): #向装饰器中传入不定长参数
4 print('%s called at %s'%(func.__name__,ctime()))
5 func(*args, **kwargs)
6 return wrapper
7 @decorator_func
8 def foo(a, b, c):
9 print (a+b+c)
10 @decorator_func
11 def foo1(a,b,c):
12 return a+b+c
13 foo(1,2,3)
14 sleep(2)
15 foo1(4,5,6)
16 sleep(2)
17 print (foo1(4,5,6))
运行结果:
foo called at Sun Nov 6 22:13:38 2016
6
foo1 called at Sun Nov 6 22:13:40 2016
foo1 called at Sun Nov 6 22:13:42 2016
None
foo1函数返回了运算结果,但是并没有对此结果进行打印或是继续返回,所以此时打印的为None。加入return之后:
1 from time import ctime,sleep
2 def decorator_func(func):
3 def wrapper(*args, **kwargs): #向装饰器中传入不定长参数
4 print('%s called at %s'%(func.__name__,ctime()))
5 return func(*args, **kwargs) #加入return
6 return wrapper
7 @decorator_func
8 def foo(a, b, c):
9 print (a+b+c)
10 @decorator_func
11 def foo1(a,b,c):
12 return a+b+c
13 foo(1,2,3)
14 sleep(2)
15 print (foo1(4,5,6))
运行结果:
foo called at Sun Nov 6 22:20:23 2016
6
foo1 called at Sun Nov 6 22:20:25 2016
15
可以在原有装饰器的基础上设置外部变量
1 from time import ctime,sleep
2 def decorator_func_arg(pre='hello'): #设置外部变量
3 def decorator_func(func):
4 def wrapper(*args, **kwargs): #向装饰器中传入不定长参数
5 print('%s called at %s %s'%(func.__name__,ctime(), pre))
6 return func(*args, **kwargs) #加入return
7 return wrapper
8 return decorator_func
9 @decorator_func_arg()
10 def foo(a, b, c):
11 print (a+b+c)
12 @decorator_func_arg('world')
13 def foo1(a,b,c):
14 return a+b+c
15 foo(1,2,3)
16 sleep(2)
17 print (foo1(4,5,6))
运行结果:
foo called at Sun Nov 6 22:29:39 2016 hello
6
foo1 called at Sun Nov 6 22:29:41 2016 world
15