一、什么是函数装饰器?
函数装饰器用于在源码中标记函数,以某种方式增强函数的行为。
装饰器是可调用对象,其参数是另外一个函数(被装饰的函数)。装饰器可能会处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。
听起来有点模糊,具体是什么意思呢?首先我们来理解一下Python中的函数。
def hi(name="jack"):
return "hi " + name
print(hi()) # output: 'hi jack'
# 我们甚至可以将一个函数赋值给一个变量,比如
greet = hi
# 我们这里没有在使用小括号,因为我们并不是在调用hi函数
# 而是在将它放在greet变量里头。我们尝试运行下这个
print(greet()) # output: 'hi jack'
del hi # 如果我们删掉旧的hi函数,看看会发生什么!
print(hi()) # outputs: NameError
print(greet()) # outputs: 'hi jack'
上面是python函数的基础知识,下面来看看装饰器在函数中的使用
#定义一个装饰器
def deco(func):
def inner():
print('hello boys')
return inner
@deco #装饰函数
def target1():
print('hi girls')
def target2():
print('hi boys and girls')
target1() #调用target1函数
x = deco(target2) # target2没加(),只作为deco装饰器的参数
x() #结果跟使用装饰器一样
可以看到target1函数使用装饰器和以target2函数作为装饰器的参数的结果是一样的。
二、 装饰器的应用场景
授权(Authorization)
装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint)。它们被大量使用于Flask和Django web框架中。以下是两个应用:
发送邮件确认身份
from functools import wraps
#不带参数的装饰器
def confirm_required(func):
@wraps(func)
def decorated_function(*args, **kwargs):
if not current_user.confirmed:
message = Markup(
'Please confirm your account first.'
'Not receive the email?'
'<a class="alert-link" href="%s">Resend Confirm Email</a>' %
url_for('auth.resend_confirm_email'))
flash(message, 'warning')
return redirect(url_for('main.index'))
return func(*args, **kwargs)
return decorated_function
使用functools模块提供的wraps装饰器可以避免被装饰函数的特殊属性被修改,比如函数名称__name__被更改,如果不使用该模块,则会导致函数名称被替换,从而导致端点值(端点的默认值即函数名)出错。
权限验证
from functools import wraps
#带参数的装饰器
def permission_required(permission_name):
def decorator(func):
@wraps(func)
def decorated_function(*args, **kwargs):
if not current_user.can(permission_name):
abort(403)
return func(*args, **kwargs)
return decorated_function
return decorator
严格来说,装饰器只是语法糖。装饰器可以像常规的可调用对象那样调用,其参数是另一个函数。
装饰器的特点:
- 能把被装饰函数替换成其它函数
- 装饰器在加载模块时立即执行。例如在hello2.py文件中导入前面有deco装饰器的hello1.py文件,当import hello1时,deco装饰器立即执行,即会输出hello boys。