python装饰器就是用来给原来的函数拓展功能的一种函数。
比如你有一段函数
import time
def demo():
time.sleep(2)
print('hello world')
假如你要给这段代码增加测试运行时间的功能,第一种方法,你可以这样写
import time
def demo():
start = time.time()
time.sleep(2)
print('hello world')
print(time.time() - start)
但是这样写需要对原函数进行改造,一般当函数比较多的时候需要一个一个重写,所以这种方法我们不提倡。这里还有第二种方法,
import time
def timer(func):
start = time.time()
func()
print(time.time() - start)
def demo():
time.sleep(2)
print('hello world')
我们不改变原函数,而是另外定义一个测试函数时间的timer函数,我们在调用时可以这样写
timer(demo)
这时候将输出
hello world
2.0021307468414307
这里demo相当于一个变量,里面存放的是函数的地址,只有加上()才表示函数的调用,我们将demo的函数地址传入timer函数,用变量func接收,则变量func和变量demo的内容相同,都是函数的地址,这时候我们将func加()可以调用demo()函数,这样就可以完成测试函数时间的功能,但是这种方法改变了函数的调用方式,之前调用函数使用demo()
变成了现在的timer(demo)
,在公司中不利于多人维护,所以我们也不推荐这样的方法。
----------------------------------------------------------------分割线----------------------------------------------------------------------
上面两种方法,在给函数添加新功能的时候,一个修改了函数的内容,一个改变了函数的调用方式,都不是最理想的方法,有没有一种方法不会改变函数内容并且不会改变函数调用方式呢?那么就是我们python装饰器发挥作用的时候了。
首先装饰器其实就是一个闭包,把一个函数当做参数然后返回一个替代版函数,关于闭包,有一个简单的解释:传送门。我们可以将上述代码改写为
import time
def timer(func): 装饰函数
def inner():
start = time.time()
func()
print(time.time() - start)
return inner
@timer 语法糖
def demo(): 被装饰函数
time.sleep(2)
print('hello world')
在这里,我们将timer写成一个闭包函数,内层函数为inner(),外层函数返回内层函数的地址,注意:函数只有加()的时候才表示调用,不加()就是保存地址的变量,这就是闭包。我们定义timer()是装饰器函数,demo()是被装饰函数,在被装饰函数demo的上方添加@timer就是装饰器的格式,其中(@装饰器函数)叫做语法糖。@timer表示执行timer函数,并将@timer下方的函数名作为参数赋值给timer函数,也就是将下放函数的地址作为参数传递到timer函数中,最后将timer函数的返回值也就是内部函数的地址重新赋值给下方函数。所以装饰器可以和下面的代码等价,只是换了一种写法,如果感觉看的不是很懂建议先搞懂函数闭包。
import time
def timer(func): 装饰函数
def inner():
start = time.time()
func()
print(time.time() - start)
return inner
def demo(): 被装饰函数
time.sleep(2)
print('hello world')
demo = timer(demo) 这里demo只是一个变量,里面可以保存任何函数的地址
demo() 通过地址调用函数
当使用了装饰器之后,这时候再调用程序
demo()
程序将会输出
hello world
2.0021307468414307
可以看到,我们一没有改变函数的内容,二没有改变函数的调用方式,就将一个新的功能赋予了函数demo,这就是python装饰器的作用。