Python装饰器的作用/意义
今天其实也有人问到了python装饰器是什么,一下子我也被问得有点懵逼了,有些基础的理论确实忘了,然而因为也是自己写的代码,也没有去做所谓的核心代码的区别,但是重新看了一下理论,还是知道了大概的一个作用是什么。
Python装饰器就是用于拓展原来函数的一种函数,在不改动原函数的代码的前提下给函数增加新的功能,这也是代码可拓展性保证了核心代码不被破坏的重要函数。而这个函数的特殊之处也是在于他的返回值也是一个函数。
一般来说,想要拓展原来的函数代码,最直接的办法就是修改原先的代码,倾入原先的代码进行修改。
例如:这个是个简单输出hello world的一个函数
import time def func(): print("hello") time.sleep(1) print("world")
现在的需求是,我们要记录一下,这个函数执行的总时间,当然最简单的方法就是:
在原有的代码上去加一些代码条件,包括去记录开始时间和结束时间,以此来计算出一个总时间。
import time def func(): startTime = time.time() print("hello") time.sleep(1) print("world") endTime = time.time() msecs = (endTime - startTime)*1000 print("time is %d ms" %msecs)
这样一来是不是比较简单,但是别慌啊,好戏在后头。在一些公司,可能这个代码不是你写的,但是现在好比说这个代码,主管(BOSS)跟你说,这段代码是核心代码,不能直接改核心代码,想办法去增加功能!
这个时候,装饰器就派上用场了,现在,我自己重新加了一个装饰器去写
我们先写一个简单的装饰器
import time def deco(func): def wrapper(): start_time = time.time() func() end_time = time.time() timer = end_time - start_time print("run time spend :", timer, 's', sep='') return wrapper def func(): print("hello") time.sleep(1) print("world") func = deco(func) func()
上面这个例子中,其实deco就是一个装饰器,它把执行业务的func()包汲取了,看起来像是被装饰了一样。
在这个例子中,函数进入和退出时,被成为一个横切面,这种编程通常被成为面向切面的变成(Aspect-Oriented Programming)简称AOP。
和面向对象编程不同的是。
这样的做法,对原有代码毫无入侵性,这就是AOP的好处了,把和主业务无关的事情,放到代码外面去做。
@符号是装饰器的语法糖,在定义函数的时候使用,避免再一次赋值操作
有些人会不明白了,语法糖又是什么?
语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。
简单的来说,就是提升效率,甚至可以带来更简洁的效果。
import time def deco(func): def wrapper(): start_time = time.time() func() end_time = time.time() timer = end_time - start_time print("run time spend :", timer, 's', sep='') return wrapper @deco def func(): print("hello") time.sleep(1) print("world") func()
可以看到,我们定义了一个函数deco,然后它的参数是一个函数,然后增了一个计时的功能。
有些人可能不明白,这个@deco是什么意思呢?
其实@deco 就是引用装饰器,相当于func = deco(func),是的,然后deco其实就是一个装饰器,也就是语法糖
@deco 在编译器的意思就等同于func = deco(func)
这样的话效率提升,简洁有效!
当然拉,语法糖不是强制要求的,目的就是为了书写简单方便。
而这里调用的方式其实还是跟之前一样。
装饰器的意义:装饰器在不改变被装饰函数的源代码和调用方式的情况下增加新的功能,所以即便是修改了原先的调用方式也是不行。
这样就可以在不动用核心代码的基础上去拓展它的函数功能!
同时,装饰器其实也是可以带入参数的。
在上一个列子中,我们已经加了一个装饰器了,那么怎样才可以带入参数呢?
单参数的例子:
import time def use_parameter(par): def deco(func): def wrapper(): start_time = time.time() func() end_time = time.time() timer = end_time - start_time if par == True: print("run time spend :", timer, 's', sep='') return wrapper return deco @use_parameter(True) def func(): print("hello") time.sleep(1) print("world") func()
双参数是以此类推,要记得,由于use_parameter里面是有两个函数的,所以两个函数都要返回相应的函数,否则就会出现报错。
还有就是类的装饰,相比函数装饰器,类装饰器具有灵活度大,高内聚、封装性等有点。使用类装饰器还可以依靠内部的一些方法。
import time class Test(object): def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): start_time = time.time() self.func() end_time = time.time() timer = end_time - start_time print("run time spend :", timer, 's', sep='') @Test def func(): print("hello") time.sleep(1) print("world") func()
用类封装装饰器的例子中看出,func已经进入了类Test的_call_的方法,init方法中记录传入的函数,然后使用call调用修饰的函数和其他的处理,这里的call也等同于之前例子中的deco()函数,当然还有使用对象作为装饰器的做法也是有的,更方便的定制和添加参数