前提知识点:
- 可以将一个函数赋值给一个变量
备注:函数用函数名标识,赋值时候不带括号,带函数括号表示调用函数/执行函数 函数名表示函数,函数名()表示执行函数。 - 可以在函数中定义另外的函数,那么在执行外层函数时候,里面的函数也会执行
- 从函数中返回函数,返回的函数不带函数括号,带函数括号表示调用、执行函数 备注:函数名表示函数,函数名()表示调用执行函数
- 将函数作为参数传给另一个函数
具体实例见:https://www.runoob.com/w3cnote/python-func-decorators.html
装饰器的作用
python函数修饰器@的作用是在不改变其它函数的情况下,为其它函数增加额外的功能,常用于插入日志、性能测试、事务处理等等。
装饰器的定义
装饰器本质上是一个python函数,他可以让其它函数在不需要做任何代码改动的情况下增加额外的功能,其传入参数一般是函数对象(如视图函数),返回值也是一个函数对象;
装饰器使用@作为装饰符,funA()就是装饰器函数,funB()为被装饰的函数。
装饰器主要用于有切面需求的场景,如插入日志、性能测试、事务处理等与函数功能无关的操作,对于这些需要多次重用的代码,我们将其放置在装饰器里,就可以无需在每个函数中反复编写;
装饰器的处理逻辑
使用的时候先要定义装饰器函数,然后在其它函数中使用装饰器函数。当执行被装饰的函数时,先执行装饰器,将被装饰的函数作为装饰器的入参,并将装饰器的返回值赋值给被装饰的函数。
例如我们要在新闻页面前进行登录操作,可以这样实现:
# 定义装饰器函数
def user_login(func):
def inner(): #嵌套1个函数执行被装饰的函数
# 替代登录操作
print('登录操作!')
# 执行传入的函数
func()
# 使用return inner,返回的是inner函数
return inner
@user_login
# 定义被装饰函数news,并在上面关联上装饰器函数,该函数将自动传给装饰器做参数
def news():
print('这是新闻详情页!')
#执行定义的函数,也即被装饰的函数
news()
过程分析:
1、解析执行news()函数时,先执行news函数的装饰器user_login,入参为函数news
2、执行user_login(news),返回inner函数,将函数的返回值赋给被装饰函数,即news=inner,那么news()=inner(),也即执行函数news(),实际执行函数inner()。
程序执行结果为:
登录操作!
这是新闻详情页!
上面展示的是不含参数的函数使用装饰器,带参数的函数我们同样也可以使用装饰器,这里要先回顾一下Python的可变参数:
def func(*args,**kwargs) :
*:代表元组,长度不限;
**:代表键值对,个数不限;
*args:指用元组传参,元组内包含不定个数的位置参数;
**kwargs:指用字典传参,字典内包含不定个数的关键字参数(键值对);
如果被装饰函数带参数,并且也有可能多个函数被同一个装饰器函数修饰,而且这些函数带有的参数个数并不相等,怎么办呢?
最简单的解决方式是用 *args 和 **kwargs 作为装饰器内部嵌套函数的参数
思路:先定义装饰器函数,将被装饰的函数做为入参,然后在装饰器函数里面嵌套定义一个函数,将*args 和 **kwargs 做为入参,嵌套
的函数执行被装饰的函数。 备注:使用带参数方式定义的装饰器去执行没有参数的被装饰函数,也没问题。
例如:
# 定义装饰器函数
def user_login(func):
# inner函数接收参数
def inner(*args,**kwargs):
print('登录操作!') # 替代登录操作
# 执行传入的入参函数时使用inner接收到的参数
func(*args,**kwargs)
return inner
#1、不带参的不受影响
@user_login
# 定义被装饰函数news,并在上面关联上装饰器函数,该函数将自动传给装饰器做参数
def news():
print('这是新闻详情页!')
#执行定义的函数,也即被装饰的函数
news()
#2、带参数的定义时预声明接收的参数,可以是*args,也可以是**kwargs
@user_login
def news_list(*args):
# 获取元组args的第一个元素
page=args[0]
print('这是新闻列表页的第'+str(page)+'页!')
#执行定义的函数,也即被装饰的函数
news_list(5)
过程分析:
1、解析执行news()函数时,先执行news函数的装饰器user_login,入参为函数news
2、执行user_login(news),返回inner函数,将函数的返回值赋给被装饰函数,即news=inner,那么news()=inner(),也即执行函数news(),实际执行函数inner()
运行结果:
登录操作!
这是新闻详情页!
登录操作!
这是新闻列表页的第5页!
说明:在装饰器函数的定义中我们return的是函数名,而不是函数名()。为什么?这是因为当你把一对小括号放在后面,这个函数就会执行,返回的是执行结果;而如果你不放括号在它后面,return的是函数,它可以被到处传递,并且可以赋值给别的变量而不去执行它。也即将函数作为参数、将函数作为返回值、将函数赋值给另一个变量等都不用带括号,只需要函数名