python基础学习——装饰器
学习python时,我提出过如下问题:
1.装饰器是什么?
2.一个@符号 + 一个函数名就是一个装饰器了?
3.什么时候要用到这是神器的工具?
到底怎么理解、怎么应用装饰器,在本文中概括一下自己的理解。
一、先举栗子,逐层分析
1 # 获取func函数的运行时间 2 import time 3 4 # 这是一个计时函数 5 def deco(func): # 传入一个函数参数 6 startTime = time.time() 7 func() 8 endTime = time.time() 9 ms = (endTime - startTime)*1000 10 print("time is {} ms".format(ms)) 11 12 def func(): 13 print("hello") 14 time.sleep(1) 15 print("decorator") 16 17 deco(f)
上面的代码里 deco 就是 func 的装饰函数,func 被当作一个参数传入 deco 方法中,返回deco里的返回值。
这就是一个装饰器的功能,不影响源代码,通过装饰器来实现需求的功能。这是我自己的理解,下面有更专业的解释。
所以,这里回答了第 1 问,装饰器到底是什么?
本质上,装饰器(decorator)就是一个返回函数的高阶函数,再本质的,装饰器其实就是一个闭包,把一个函数当做参数然后返回一个替代版函数。
python里的闭包:Python支持一个叫做函数闭包的特性,用人话来讲就是,嵌套定义在非全局作用域里面的函数能够记住它在被定义的时候它所处的封闭命名空间。-- 摘自“12步轻松搞定装饰器”
注:这里涉及到闭包的概念,它不只存在于 python 里,几乎目前流行的面向对象的编程语言中都不同程度和不同形式的支持闭包,这里不多做解释,请自行百度。
下面通过代码示例说明本质上的装饰器:
1 # 还是上面的例子,这里进行改进 2 import time 3 4 def deco(func): 5 def wrapper(): 6 startTime = time.time() 7 func() 8 endTime = time.time() 9 ms = (endTime - startTime)*1000 10 print("time is {} ms".format(ms)) 11 return wrapper 12 13 @deco 14 def func(): 15 print("hello") 16 time.sleep(1) 17 print("decorator") 18 19 func()
将 deco 函数改为高阶函数,并用语法糖的形式装饰 func 函数(这里的@deco暂不解释。),实现的效果与上一例相同也不同,具体解释如下:
很明显,‘装饰’的方法不同,上一例用 deco 调用 func,本例直接调用 func 函数,返回结果一样,但是返回值并不相同,一个返回函数内部参数值,一个返回函数。
二、一个@符号 + 一个函数名就是一个装饰器了?
Python2.4支持使用标识符@将装饰器应用在函数上,只需要在函数的定义前加上@和装饰器的名称。只是加了一些语法糖让装饰的行为更加的简洁明确和优雅一点。
其实它本来的样子可以是这样:
1 # 还是上面的例子,这里进行改进 2 import time 3 4 def deco(func): 5 def wrapper(): 6 startTime = time.time() 7 func() 8 endTime = time.time() 9 ms = (endTime - startTime)*1000 10 print("time is {} ms".format(ms)) 11 return wrapper 12 13 def func(): 14 print("hello") 15 time.sleep(1) 16 print("decorator") 17 18 func = deco(func) 19 func()
是不是还是上面的简洁明了一点,当然了这只是装饰器写法上的差别,只要理解了本质,就不用被 ’@标识符’ 蒙住。
三、什么时候要用到这神奇的工具?还有什么花里胡哨的用法?
坦率的讲,我在目前的编程中并未使用过装饰器,只是在阅读代码的时候见到,既然这么频繁的出现,说明必有大用。以后如有具体的项目实践,我再来更新。
说是花里胡哨的用法,其实就是它神奇的参数传递,我相信,这就是它经常被使用的原因之一,结合闭包的功能的理解,就会发现其神奇之处。
代码来说事:
1 import time 2 3 def deco(func): 4 def wrapper(a, b): 5 startTime = time.time() 6 func(a, b) 7 endTime = time.time() 8 ms = (endTime - startTime)*1000 9 print("time is {} ms".format(ms)) 10 return wrapper 11 12 13 @deco 14 def func(a, b): # 要装饰的函数需要传入参数 15 print("hello,here is a func for add :") 16 time.sleep(1) 17 print("result is {}".format(a+b))
18
19 func(2, 3)
1、当它被用作多个函数的装饰器的时候,不确定其参数个数的多少,还可以用 python 的可变参数来定义,如下:
1 # 装饰多个函数 2 import time 3 4 def deco(func): 5 def wrapper(*args, **kwargs): 6 startTime = time.time() 7 func(*args, **kwargs) 8 endTime = time.time() 9 ms = (endTime - startTime)*1000 10 print("time is {} ms".format(ms)) 11 return wrapper 12 13 @deco 14 def func1(a,b): 15 print("hello,here is a func for add :") 16 time.sleep(1) 17 print("result is {}".format(a+b)) 18 19 @deco 20 def func2(a,b,c): 21 print("hello,here is a func for add :") 22 time.sleep(1) 23 print("result is {}".format(a+b+c))
24
25 func1(1, 2)
26 func2(1, 2, 3)
2、应用多个装饰器,实现多个功能,通过以下例子理解它的实现过程(本实例通过 ipython 实现,直接复制粘贴出来的):
In [17]: import time ...: ...: def deco1(func): ...: def wrapper(*args, **kwargs): ...: print("I am deco1") ...: startTime = time.time() ...: func(*args, **kwargs) ...: endTime = time.time() ...: ms = (endTime - startTime)*1000 ...: print("time is {} ms".format(ms)) ...: print("deco1 is done") ...: return wrapper ...: ...: def deco2(func): ...: def wrapper(*args, **kwargs): ...: print("I am deco2") ...: func(*args, **kwargs) ...: print("deco2 is done") ...: return wrapper ...: ...: @deco01 ...: @deco02 ...: def func(a,b): ...: print("Here is a + b:") ...: time.sleep(1) ...: print("result is {}".format(a+b)) ...: In [18]: func(1, 2) I am deco1 I am deco2 Here is a + b: result is 3 deco2 is done time is 1004 ms deco1 is done
总结本例:多个装饰器时,先调用第一个 deco,再调用第二个 deco,后调用函数自己,再依次退出(先函数自己,再第二个deco,最后第一个deco)。
总结:
以上则是我自己对装饰器的理解,并列出了相应的实例以作解释,记在这当回顾,分享给大家以作交流,欢迎留言。
注:本文参考了以下文章,特此感谢。