装饰器的实质就是一个函数,可以对其他函数进行装饰,在不改变原函数代码的基础上增加新的功能,调用方式也不改变。
比如我们有两个函数sout1,sout2
import timedef sout1(): time.sleep(1) print("this is sout1") def sout2(): time.sleep(2) print("this is sout2") sout1()sout2()# 输出# this is sout1# this is sout2
现在我想让每个方法执行完时输出运行需要的时间
我们可以修改代码实现
import timedef sout1(): start = time.time() time.sleep(1) print("this is sout1") end = time.time() print("running time is",end-start) def sout2(): start = time.time() time.sleep(2) print("this is sout2") end = time.time() print("running time is",end-start) """输出this is sout1running time is 1.0009281635284424this is sout2running time is 2.0002005100250244"""
一两个这样功能简单的还能改改,但要是许多函数,新增的功能复杂怎么办?
最好使用装饰器来实现。
在使用装饰前我们要先了解两个概念 高阶函数、嵌套函数
在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:
接受一个或多个函数作为输入
输出一个函数
我们已经定义了sout1这个函数
sout1() // 会调用函数"""输出this is sout1running time is 1.0009281635284424"""print (sout)
万物皆对象嘛
所以函数也是对象
sout1存的是地址 是对象的引用
我们新增一个引用
hello = sout1hello()"""输出this is sout1running time is 1.0009281635284424"""
既然是对象 当然也能作为函数的参数和返回值
例:
def test(func): return funcdef sout(): print("hello world!") a = test(sout)a()"""输出hello world!"""
嵌套函数
就是函数里面又定义一个函数
例:
def outer(): print("hello") def inner(): print("world!") inner()outer()"""输出helloworld!"""
实现
新增功能(修改调用方式)
既然可以把函数当作参数传入,那..
我们不就可以把原有函数当作参数传进一个新的函数中,新增点功能在返回出来?
import time def sout1(): time.sleep(1) print("this is sout1") def decorate(func): start = time.time() func() end = time.time() print("running time is",end-start) decorate(sout1) """ 输出 this is sout1 running time is 1.0004260540008545 """
现在已经实现了不修改原有代码添加新功能了,但是调用方式和以前不一样了。
新增功能(不修改调用方式)
那怎么实现不修改原来的调用方式?还是sout1()进行调用?
我们已经知道函数也可以和变量一样,给它一个新的引用,进行调用
如:
x = 1y = xprint(y)"""输出1"""def sout1(): time.sleep(1) print("this is sout1") hello = sout1hello()"""输出this is sout1"""
那我们想办法让进行装饰的函数 decorate() 也返回一个函数
然后 sout1 = decorate(sout1)
不就实现了sout1()
新增功能而不修改原代码,原调用方式?
所以我们可以把decorate()中的代码再封装一下
def sout1(): time.sleep(1) print("this is sout1")# 原decorate()# def decorate(func):# start = time.time()# func()# end = time.time()# print("running time is",end-start)#改为:def decorate(func): def inner(): start = time.time() func() end = time.time() print("running time is",end-start) return innersout1 = decorate(sout1)sout1()"""输出this is sout1running time is 1.0003764629364014"""
成功!
然而我们还是要在 原来的函数调用前
添加类似语句 sout1 = decorate(sout1)
这个事情python可以通过语法糖@来帮我们完成
def decorate(func): def inner(): start = time.time() func() end = time.time() print("running time is", end - start) return inner@decorate # 等同于 sout1 = decorate(sout1)def sout1(): time.sleep(1) print("this is sout1")sout1()