Python中的装饰器是一种特殊的语法,可以在不改变被装饰函数原有代码的基础上,为函数添加新的功能或修改其行为。装饰器本身可以看作是一个返回函数的函数,它的作用是接受一个函数作为参数,然后返回一个新的函数,这个新函数可以执行被装饰函数的原有代码,也可以在执行前后添加新功能。
这里打个比喻:每个人都有的内裤主要功能是用来遮羞,但是到了冬天它没法为我们防风御寒,咋办?我们想到的一个办法就是把内裤改造一下,让它变得更厚更长,这样一来,它不仅有遮羞功能,还能提供保暖,不过有个问题,这个内裤被我们改造成了长裤后,虽然还有遮羞功能,但本质上它不再是一条真正的内裤了。于是聪明的人们发明长裤,在不影响内裤的前提下,直接把长裤套在了内裤外面,这样内裤还是内裤,有了长裤后宝宝再也不冷了。装饰器就像我们这里说的长裤,在不影响内裤作用的前提下,给我们的身子提供了保暖的功效。
装饰器的作用实际上就是在不改变函数原有代码的情况下,为它添加新的功能。这可以让我们更加灵活地使用函数,并且避免出现重复代码的情况。常见的装饰器功能包括添加日志、缓存计算结果、检查函数参数等。
函数装饰器分为:无参装饰器和有参装饰两种,二者的实现原理一样,都是’函数嵌套+闭包+函数对象’的组合使用的产物。
如果想为以下函数添加时间统计功能
import time
def index():
time.sleep(3)
print('hellow')
return 10
index() #函数执行步骤
遵循不修改被装饰源代码的情况下,我们想到的是这样的
start_time = time.time()
index() #函数执行步骤
end_time = time.time()
print('函数代码执行时间%s' % (end_time - start_time))
import time
def index():
time.sleep(3)
print('hellow')
def outer(func):
def inner():
start_time = time.time()
res = func()
end_time = time.time()
print('函数代码执行时间:%s' % (start_time - end_time))
return res
return inner
这样我们便可以在不修改被装饰函数源代码和调用方式的前提下为其加上统计时间的功能,只不过需要事先执行一次outer将被装饰的函数传入,返回一个闭包函数inner重新赋值给变量名 /函数名index,如下
index=outer(index) #得到index=inner,inner携带对外作用域的引用:func=原始的index
index() # 执行的是inner(),在inner的函数体内再执行最原始的index
至此我们便实现了一个无参装饰器timer,可以在不修改被装饰对象index源代码和调用方式的前提下为其加上新功能。
但我们忽略了若被装饰的函数是一个有参函数,便会抛出异常
def index1(name):
time.sleep(2)
print('hellow', name)
index1 = outer(index1)
index1('world') #TypeError: index1() missing 1 required positional argument: 'name'
之所以会抛出异常,是因为index1(‘world’)调用的其实是inner(‘world’),而函数inner没有参数。inner函数接收的参数其实是给最原始的func用的,为了能满足被装饰函数参数的所有情况,便用上*args+**kwargs组合,于是修正装饰器timer如下
def outer(func):
def inner(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
end_time = time.time()
print('函数代码执行时间:%s' % (end_time - start_time))
return res
return inner
def outer(func):
def inner(*args, **kwargs)
res = func(*args, **kwargs)
return res
return inner
Python中的装饰器语法糖是使用 @ 符号来应用装饰器的一种简化写法。使用装饰器语法糖可以使代码更加简洁易读。
下面是一个使用装饰器语法糖的示例:
def outer(func):
def inner(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
end_time = time.time()
print('函数代码执行时间:%s' % (end_time - start_time))
return res
return inner
@outer
def index():
time.sleep(3)
print('hellow')
在上面的代码中,我们使用 @ 符号将outer装饰器应用到index函数上面。这样就相当于执行了以下代码:
def index():
time.sleep(3)
print('hellow')
index = outer(index)
最终输出结果与之前相同
装饰器语法糖使得代码更加简洁易读,特别是在应用多个装饰器时,可以避免过多的嵌套,使代码更加易于维护。同时,使用装饰器语法糖还能减少编码量,并提高代码的可读性。