装饰器是python基础语法中比较晦涩难懂的地方,借此机会,谈谈自己对装饰器的理解。
一、什么是装饰器
装饰:给别的函数添加功能
器:就是具有某种作用的工具
装饰器:给其他函数添加功能的工具,本质也是函数,而且是一个闭包函数。
二、为什么要有装饰器
一、函数传参的另外一种形式
之前我们学的函数传参都是类似于如下:
def func(a,b):
print(a,b)
有了装饰器之后我们可以这样传参:
这里是为deco传的参数,这里的deco就是一个装饰器,他需要一个参数是func,即被装饰函数的地址
def wrapper(func):
def deco(*args,**kwargs):
print('in the wrapper')
res = func(*args,**kwargs)
return res
return deco
def func2():
print(123)
那为什么不这样写?
def deco(func,*args,**kwargs):
print('in the wrapper')
res = func(*args,**kwargs)
return res
我将在下面解释
二、在不改变函数源代码和调用方式的情况下为函数添加功能,具体用法在下面介绍
三、怎么用装饰器
def wrapper(func):
def deco(*args,**kwargs):
print('in the wrapper')
res = func(*args,**kwargs)
return res
return deco
def func2():
print(123)
前面已经说过,deco就是一个装饰器,本质是闭包函数。对于闭包函数简单一提,在python中万物皆对象,因此函数也可以作为其他函数的参数、返回值、也可以作为容器类型的元素
x = 4
def deco():
x = 1
def func():
print(x)
闭:可以理解为一个麻袋把函数套起来,这里的麻袋就是deco
包:可以理解为麻袋里面的东西,这里的x就是麻袋里的东西,这里打印的x是1
闭包函数的名字的查找关系是以函数定义阶段为准,可以理解为局部变量覆盖了全局变量
四、语法糖
形式:@函数名
def wrapper(func):
def deco(*args,**kwargs):
print('in the wrapper')
res = func(*args,**kwargs)
return res
return deco
@wrapper
def func2():
print(123)
这里func2被deco装饰,@wrapper等价于func2 = wrapper(func2),为func2多加了一个功能,即打印了in the wrapper,那为什么不能在定义deco的时候多一个函数参数呢,那是因为实际上func2变成了 deco,真正调用func2的时候会是这样
func2()
func2被装饰后,运行的就是deco了,但是用户丝毫不清楚这个过程,因为调用方式没变,函数(指的是func2)的代码也没变,如果加上一个函数参数,那么在调用的时候会出错,说是少了一个参数。这里的deco参数和func2的函数应该保持一致。
有参装饰器,简单一提(其实可以不用语法糖实现,省略,因为比较low)
def outter(x):
def wrapper(func):
print(x)
def deco(*args,**kwargs):
print('in the wrapper')
res = func(*args,**kwargs)
return res
return deco
return wrapper
@outter(1)
def func2():
print(123)
func2()
多层装饰器的加载顺序是从下往上,运行顺序是从上往下。
def deco1(func1): # func1 = wrapper2的内存地址
def wrapper1(*args,**kwargs):
print('正在运行===>deco1.wrapper1')
res1=func1(*args,**kwargs)
return res1
return wrapper1
def deco2(func2): # func2 = wrapper3的内存地址
def wrapper2(*args,**kwargs):
print('正在运行===>deco2.wrapper2')
res2=func2(*args,**kwargs)
return res2
return wrapper2
def deco3(x):
def outter3(func3): # func3=被装饰对象index函数的内存地址
def wrapper3(*args,**kwargs):
print('正在运行===>deco3.outter3.wrapper3')
res3=func3(*args,**kwargs)
return res3
return wrapper3
return outter3
# 加载顺序自下而上(了解)
@deco1 # index=deco1(wrapper2的内存地址) ===> index=wrapper1的内存地址
@deco2 # index=deco2(wrapper3的内存地址) ===> index=wrapper2的内存地址
@deco3(111) # ===>@outter3===> index=outter3(index) ===> index=wrapper3的内存地址
def index(x,y):
print('from index %s:%s' %(x,y))
# 执行顺序自上而下的,即wraper1-》wrapper2-》wrapper3
index(1,2) # wrapper1(1,2)
更加伪装:
from functools import wraps
def outter(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""这个是主页功能"""
res = func(*args, **kwargs) # res=index(1,2)
return res
# 手动将原函数的属性赋值给wrapper函数
# 1、函数wrapper.__name__ = 原函数.__name__
# 2、函数wrapper.__doc__ = 原函数.__doc__
# wrapper.__name__ = func.__name__
# wrapper.__doc__ = func.__doc__
return wrapper
讲解链接:
python