【装饰器】
本节资料来源:
什么是装饰器?
器即函数
装饰即修饰,意指为其他函数添加新功能
装饰器定义:本质就是函数,功能是为其他函数添加新功能
装饰器需要遵循的原则
1.不修改被装饰函数的源代码(开放封闭原则)
2.为被装饰函数添加新功能后,不修改被修饰函数的调用方式
实现装饰器知识储备
装饰器=高阶函数+函数嵌套+闭包
高阶函数
高阶函数定义:
1.函数接收的参数是一个函数名
2.函数的返回值是一个函数名
3.满足上述条件任意一个,都可称之为高阶函数
举例一:
--函数的参数是一个函数名--
实现在不修改源代码的情况下为foo函数添加功能,但是改变了foo函数的调用方式:
(原先调用foo函数是用foo()这种方法,但是添加了“计算时间”的新功能之后,调用方式变成"test(foo)")
import time
def foo(): #这不是一个高阶函数
time.sleep(3)
print("你好啊林师傅")
def test(func): #这是一个高阶函数(形参接受的值是一个函数名)
print(func) #<function foo at 0x000001A7E80A36A8>打印了函数foo的内存地址
start_time = time.time()
func() #你好啊林师傅
stop_time = time.time()
print('函数运行时间是 %s' %(stop_time - start_time)) #函数运行时间是 3.000262498855591
test(foo)
举例二:
--函数的返回值是一个函数名--
实现“不改变函数的调用方式”
def foo():
print('from the foo')
def test(func):
return func
foo = test(foo)
foo() #from the foo
启发:将上述两者结合就能既在不修改原代码的情况下添加新功能,又不改变函数的调用方式
举例三:既在不修改原代码的情况下添加新功能,又不改变函数的调用方式
import time
def foo():
time.sleep(3)
print('from the foo')
def timer(func):
start_time = time.time()
func()
stop_time = time.time()
print('函数运行时间是 %s' % (stop_time - start_time))
return func #拿到func函数的运行地址
foo = timer(foo) #把func函数的地址赋值给foo
foo()
输出:
from the foo
函数运行时间是 3.0008068084716797
from the foo
但是,举例三多运行了一步,不合格
结论:高阶函数解决不了这个问题
函数嵌套
什么是函数的嵌套?
#在一个函数内部调用另外一个函数,不叫函数的嵌套
def bar():
print('from bar')
def foo():
print('from foo')
bar()
#在一个函数内部定义另一个函数,叫做函数的嵌套
def bar():
print('from bar')
def foo():
print('from foo')
def test():
pass
举例一:一个函数嵌套的例子
def father(name):
print('from father %s' %name)
def son():
print('from son')
def grandson():
print('from grandson')
grandson()
son()
father('林海峰')
输出:
from father 林海峰
from son
from grandson
举例二:函数也是一个变量
def father(name):
print('from father %s' %name)
def son(): #函数也是一个变量
print('from son')
print(locals()) #打印当前层的局部变量
father('alex')
输出:
from father alex
{'son': <function father.<locals>.son at 0x00000244C4963620>, 'name': 'alex'}
注意:
这个例子中的son是局部变量,只能在局部调用
举例三:函数嵌套要注意作用域
def father(name):
#print('from father %s' %name)
def son():
print('我的爸爸是%s' %name) #当前层级没有name变量,所以去上一级找
def grandson():
name = 'alex'
print('我的爷爷是%s' %name)
grandson() #当前层级有name变量,所以用alex
#print(locals())
son()
father('linhaifeng')
输出:
我的爸爸是linhaifeng
我的爷爷是alex
闭包
什么是闭包?闭包就是之前讲过的作用域的一种体现
最外层的变量可以传入最里层
装饰器实现
import time
#装饰器的架子
def timmer(func): #func = test
def wrapper():
#print(func)
start_time = time.time()
func() #当前层级没有变量func,所以往上一层找。这一句就是在运行test()
stop_time = time.time()
print('运行时间为 %s' %(stop_time-start_time))
return wrapper #return的是嵌套在函数里的那个函数
@timmer
def test(): #test是被修饰函数
time.sleep(3)
print('test函数运行完毕')
#下面两行是改变了函数调用方式的情况
# res = timmer(test) #返回的是wrapper的地址
# res() #运行wrapper函数
#下面两行是没有改变函数调用方式的情况
# test = timmer(test) #返回的是wrapper的地址
# test() #运行wrapper函数
#这是一个更方便的方法:在被修饰函数之前加上@timmer
#@timmer就相当于 test = timmer(test)
#在被修饰的函数开投行加上@timmer
注意:要理解装饰器运行时每一句语句的运行顺序!!(视频:Day20-10)
加上被修饰的函数的返回值
返回值的例子一:
import time
#装饰器的架子
def timmer(func): #func = test
def wrapper():
start_time = time.time()
func() #当前层级没有变量func,所以往上一层找。这一句就是在运行test()
stop_time = time.time()
print('运行时间为 %s' %(stop_time-start_time))
return '123'
return wrapper
@timmer #test = timmer(test)
def test(): #test是被修饰函数
time.sleep(3)
print('test函数运行完毕')
return '这是test的返回值'
res = test() #实际上运行的是timmer的返回值wrapper
print(res) #123
返回值的例子二:
import time
#装饰器的架子
def timmer(func): #func = test
def wrapper():
start_time = time.time()
res = func()
stop_time = time.time()
print('运行时间为 %s' %(stop_time-start_time))
return res
return wrapper
@timmer #test = timmer(test)
def test(): #test是被修饰函数
time.sleep(3)
print('test函数运行完毕')
return '这是test的返回值'
res = test() #实际上运行的是timmer的返回值wrapper
print(res) #这是test的返回值
加上参数
import time
#装饰器的架子
def timmer(func): #func = test
def wrapper(*args,**kwargs): #可变长传值。因为被修饰的函数的参数是不固定的,所以不能写死
start_time = time.time()
res = func(*args,**kwargs) #可变长传值
stop_time = time.time()
print('运行时间为 %s' %(stop_time-start_time))
return res
return wrapper
@timmer #test = timmer(test)
def test(name,age): #test是被修饰函数
time.sleep(3)
print('test函数运行完毕,名字是【%s】 年龄是【%s】' %(name,age))
return '这是test的返回值'
@timmer #test = timmer(test)
def test1(name,age,gender): #test是被修饰函数
time.sleep(3)
print('test函数运行完毕,名字是【%s】 年龄是【%s】 性别是【%s】' %(name,age,gender))
return '这是test的返回值'
test('linhaifeng',18) #实际上运行的是timmer的返回值wrapper
test1('linhaifeng',18,'male')
当运行
test('linhaifeng',18)
的时候,对于
def wrapper(*args,**kwargs):
这句语句,*args接收的是('linhaifeng')这个元组;*kwargs接收的是{'age':18} 这个字典,所以
res = func(*args,**kwargs)
相当于
func(*('linhaifeng'),**{'age':18})
其实就是原封不动地往下传值