一个函数被定义完成后,甚至程序发布后,后期可能需要添加某些功能,但是我们不可能每次都去修改原函数的代码,这时候装饰器就可以上场了,本篇文章将会用一个个可实现的代码,由浅入深、循序渐进得阐述装饰器的强大之处
装饰器定义、作用、原则:
- 定义: 一个装饰函数的函数,即为其他函数添加附加功能
- 作用: 给函数添加新功能,但是原来的函数代码不会被更改
- 原则: 不修改被修饰函数的源代码,不修改被修饰函数的调用方式
装饰器=高阶函数+函数嵌套+闭包
-
高阶函数: 函数接收的参数是一个函数名 或者 函数的返回值是一个函数名
-
未使用装饰器:
import time
def test(s):
start_time = time.time()
res = 0
for i in s:
time.sleep(0.1) #沉睡10秒
res += i
stop_time = time.time()
print('函数的运行时间是{}秒'.format(stop_time - start_time))
return res
print(test(range(100))) #运行函数,传入一个从0到100的范围
运行结果:
函数的运行时间是10.012572765350342秒
4950
- 高阶函数:
import time
def foo():
time.sleep(3)
print('大海如此宽广,总有一天你会遇见真心的伙伴')
def test(func): #传入的参数是一个函数,即为高阶函数
print(func) #输出函数的内存地址
func() #调用形参函数
test(foo) #调用高阶函数
运行结果:
<function foo at 0x02BF7B70>
大海如此宽广,总有一天你会遇见真心的伙伴
- 带修饰的高阶函数(不修改原函数源代码):
import time
def foo():
time.sleep(3)
print('大海如此宽广,总有一天你会遇见真心的伙伴')
def test(func): #传入的参数是一个函数,即为高阶函数
start_time = time.time()
print(func) #输出函数的内存地址
func() #调用形参函数
stop_time = time.time()
print('函数运行时间为 %s' %(stop_time - start_time))
test(foo) #调用高阶函数
运行结果:
<function foo at 0x004DC5D0>
大海如此宽广,总有一天你会遇见真心的伙伴
函数运行时间为 3.0281732082366943
- 带修饰的高阶函数(不修改原函数调用方式):
import time
def foo():
time.sleep(3)
print('大海如此宽广,总有一天你会遇见真心的伙伴')
def test(func): #传入的参数是一个函数,即为高阶函数
return func #函数的返回值为函数名,也称为高阶函数
foo = test(foo) #用foo接收test运行的返回值
foo()
运行结果:
大海如此宽广,总有一天你会遇见真心的伙伴
- 不修改源代码也不修改调用方式:
import time
def foo():
time.sleep(3)
print('大海如此宽广,总有一天你会遇见真心的伙伴')
def timmer(func): #作为一个修饰函数
start_time = time.time()
func()
stop_time = time.time()
print('函数运行时间为 %s' %(stop_time - start_time))
return func #函数的返回值为函数名,也称为高阶函数
foo = timmer(foo) #将原函数名作为接收的变量,被修饰后,就不会修改原函数的调用方式
foo()
#这样调用后,其实是会连带被修饰的结果一起输出,因为上一句赋值时已经调用了修饰函数
运行结果:
大海如此宽广,总有一天你会遇见真心的伙伴
函数运行时间为 3.018172264099121
大海如此宽广,总有一天你会遇见真心的伙伴
但是这样,它仅仅用了高阶函数,还是满足不了装饰器的功能,因为它多运行了一步
- 函数嵌套:
一个函数里定义多个函数(这里需要用到函数作用域和函数嵌套的知识,在我的这篇文章里有从浅入深的详细分析:初学者python笔记(函数))
def father(name):
#name = '白胡子_1' #三个地方对局部变量的修改
def son():
#name = '白胡子_2'
def grandson():
#name = '白胡子_3'
print('我的老爹是{}'.format(name)) #离它最近的局部变量的修改
grandson()
son() #运行嵌套函数
father('白胡子')
#里面的变量是一个一个层级的关系,从形参,可以一直传到底层的函数中再调用
运行结果:
我的老爹是白胡子
- 基本装饰器的实现(带语法糖):
import time
def timmer(func):
def wrap():
start_time = time.time() #给装饰器加的功能
func() #在装饰器中运行函数
stop_time = time.time()
print('函数运行了{}秒'.format(stop_time - start_time))
return wrap #函数的返回值是函数,所以为高阶函数
@timmer #语法糖的使用,使原函数即foo函数被装饰。相当于foo = timmer(foo)语句
def foo():
time.sleep(3)
print('test函数运行完毕!')
foo() #运行原函数,方式不变,满足原则
运行结果:
test函数运行完毕!
函数运行了3.0251729488372803秒
- 能得到原函数返回值的装饰器:
import time
def timmer(func):
def wrap():
start_time = time.time() #给装饰器加的功能
res = func() #在装饰器中运行函数,并且用一个变量来接收以返回原函数的返回值
stop_time = time.time()
print('函数运行了{}秒'.format(stop_time - start_time))
return res #返回原函数的返回值
return wrap #函数的返回值是函数,所以为高阶函数
@timmer #语法糖的使用,使原函数即foo函数被装饰。相当于foo = timmer(foo)语句
def foo():
time.sleep(3)
print('test函数运行完毕!')
return '这是test的返回值' #为了实现把原函数的返回值也接收到
foo() #运行原函数,方式不变,满足原则
print(foo()) #输出原函数的返回值
运行结果:
test函数运行完毕!
函数运行了3.0211730003356934秒
test函数运行完毕!
函数运行了3.0581750869750977秒
这是test的返回值
以上就是从装饰器的基本原理一步一步剖析,直到一个装饰器的实现,理解起来并不难。基本原理清楚了,想要深入一步学习装饰器的话,下一篇文章:初学者python笔记(装饰器后篇:登陆验证)中还有案例分析