装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。
它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
示例场景:
公司后端同学已经写好了两个接口供前端小伙伴使用,这两个接口没有做任何限制,可以直接调用。有一天老大告诉后端的同学:从现在开始,你们开发的每个接口对外提供调用的时候都要做身份验证,使用user:passwd的方式;验证不通过就不允许调用接口。
接口:
#获取水果的价格
def get_price(name):
fruit = {'apple':5,'oriange':3}
return fruit[name]
print(get_price('apple'))
现在我们要求传入user,passwd才能调用这个接口。最好的办法就是加装饰器,给函数增加功能。
#装饰器函数
def verifier_decorator(func):
def wrapper(user,passwd,*args,**kwargs):
pass_dict = {'eli':'123', 'wang':'456'}
if (user,passwd) in pass_dict.items():
print('Success')
return func(*args,**kwargs)
else:
print('Failed')
return wrapper
#用@语法糖调用装饰器
@verifier_decorator
def get_price(name):
fruit = {'apple':5,'oriange':3}
return fruit[name]
print(get_price('wang','456','apple'))
可以看到,在增加装饰器后,在调用get_price的时候要传入三个参数。是因为前两个参数被装饰器中的闭包函数接收了。
@verifier_decorator
def get_price(name):
#上面的语法导致调用get_price的时候实际效果是:
get_price = verifier_decorator(get_price)
当实际调用的时候,此接口就非彼接口了,解释器真正执行的其实是装饰器中的闭包函数,所以调用的时候填多个参数也不会报错。可以理解为,装饰器取走了你定义的接口函数,又还了一个“一模一样”的函数给你。但这个过程中装饰器又干了点别的事情。【取走--原基础上添加其他代码--再还回】这一整个过程称为“装饰”。
当然,实际上它并不会还回一个“一模一样”的函数给你,装饰的过程中,原函数的元信息会被闭包函数替换掉:
def deco(func):
def wrapper(name):
'''I am wrapper'''
return func(name)
return wrapper
@deco
def print_name(name):
'''''I am func'''
print(print_name.__doc__)
print(print_name.__name__)
print(name)
print_name('eli')
#I am wrapper
# wrapper
# eli
你如果明白了刚才说的“装饰”过程,这个地方就很容易理解了。即:真正执行的函数是装饰器中的闭包函数(wrapper):print_name=deco(print_name)真正执行的是wrapper。
那应该如何保留原函数的元信息?
Python标准库中的functools的wraps方法会在装饰器运行的时候将原函数的元信息一同传递给闭包函数,进而使得原函数对象的所有信息得以保留。
from functools import wraps #<<<---
def deco(func):
@wraps(func) #<<<---
def wrapper(name):
'''''I am wrapper'''
return func(name)
return wrapper
@deco
def print_name(name):
'''''I am func'''
print(print_name.__doc__)
print(print_name.__name__)
print(name)
print_name('eli')
#I am func
# print_name
# eli
网友评论