一、看一个例子,来引出装饰器
需求:在不改变下面 test()
函数的情况下,执行函数的情况下先进行其他的步骤,看下面的实现
def set_func(func): print("----开始装饰----") def run_func(): print("----验证权限1----") func() return run_func def test1(): print("----test----") test1 = set_func(test1) test1()
执行结果是:
----开始装饰---- ----验证权限1---- ----test----
- 分析:在
test1 = set_fun(test1)
改变了test1()
的指向,指向了闭包内的run_func()函数
,在test()
执行的时候,执行的是run_func()函数
内的代码 - 优化: 不想写
test1 = set_fun(test1)
这句代码,我们可以在def test1():
上面写@set_fun
,我们在下面具体看使用。
二、装饰器的介绍以及参数
- 2.1、简单的来说:一个闭包对一个函数进行装饰就组成了装饰器,如下的例子,
@set_fun
是一种 语法糖
def set_func(func): print("----开始装饰----") def run_func(): print("----验证权限1----") func() return run_func @set_func # @set_fun 与 test1 = set_func(test1) 等价 def test1(): print("----test----") test1()
提示: @set_fun 与 test1 = set_fun(test1) 等价
- 2.2、装饰器对有 单个参数 函数的装饰(说白了就是传参)
def set_func(func): print("----开始装饰----") def run_func(num): print("----验证权限1----") func(num) return run_func @set_func def test1(num): print("----test----%d"%num) test1(3)
提示:test1指向的是闭包内的
run_func
函数,传的参数也是给run_func,函数内再调用test1函数,不要绕,好好理解下
- 2.3、不定长参数的函数装饰(说白了就是参数不定),如下例子
def set_func(func): print("----开始装饰----") def run_func(*args,**kwargs): print("----验证权限1----") func(*args,**kwargs) return run_func @set_func def test1(num,*args,**kwargs): print("----test----%d"%num,args,kwargs) test1(3,2,1,a="1")
- 打印结果是:
----开始装饰---- ----验证权限1---- ----test----3 (2, 1) {'a': '1'}
注意:
- 闭包 里面的
func(*args,**kwargs)
中的*args
与**kwargs
是解包的意思,传参不能直接传元组与字典,要解包- 不可以传
func(args,kwargs)
,必挂,这样是传了两个参数,一个元组,一个字典,是不对的
- 2.4、对应有返回值函数进行装饰、通用装饰器
def set_func(func): print("----开始装饰----") def run_func(*args,**kwargs): print("----验证权限1----") return func(*args,**kwargs) # 拆包 return run_func @set_func def test1(num,*args,**kwargs): print("----test----%d"%num,args,kwargs) return "OK" @set_func def test2(): return "OK2" test1(3,2,1,a="1") print(test2())
- 打印结果是:
----开始装饰---- ----开始装饰---- ----验证权限1---- ----test----3 (2, 1) {'a': '1'} ----验证权限1---- OK2
- 2.5、多个装饰器对同一个函数进行装饰
def set_func1(func): print("----开始装饰1----") def run_func1(*args,**kwargs): print("----验证权限1----") func(*args,**kwargs) # 拆包 return run_func1 def set_func2(func): print("----开始装饰2----") def run_func2(*args,**kwargs): print("----验证权限2----") func(*args,**kwargs) # 拆包 return run_func2 @set_func1 @set_func2 def test1(num,*args,**kwargs): print("----test----%d"%num,args,kwargs) test1(3,2,1,a="1")
- 打印结果:
----开始装饰2---- ----开始装饰1---- ----验证权限1---- ----验证权限2---- ----test----3 (2, 1) {'a': '1'}
提示:当多个装饰器装饰同一个函数的时候,装饰从下往上装饰,也就是上面例子中先装饰
set_func2
,再装饰set_func1
装饰器在调用之前就已经装好了,组成一个大的闭包空间;执行的时候,先执行后装饰的装饰器里面的代码,后执行前一个被装饰的代码,依次类推,最后执行第一个装饰的闭包内的代码。
- 2.6、对于同一个装饰器,对多个函数进行装饰
分析:对于同一个装饰器,对多个函数进行装饰的时候,在没调用之前就会进行装饰,每装饰一个就生成一个闭包。闭包内外部函数的变量指向原函数
def set_func(func): print("----开始装饰----") def run_func(*args,**kwargs): print("----验证权限----") func(*args,**kwargs) # 拆包 return run_func @set_func def test1(num,*args,**kwargs): print("----test1----%d"%num,args,kwargs) @set_func def test2(num,*args,**kwargs): print("----test2----%d"%num,args,kwargs) test1(3,2,1,a="1") test2(4,5,6,a="2")
- 打印结果:
----开始装饰---- ----开始装饰---- ----验证权限---- ----test1----3 (2, 1) {'a': '1'} ----验证权限---- ----test2----4 (5, 6) {'a': '2'}
- 2.7、用类对函数进行装饰
class Test(object): """docstring for ClassName""" def __init__(self, func): self.func = func def __call__(self): print("这里是装饰器添加的功能") return self.func() @Test # 相当于 get_str = Test(get_str) def get_str(): return "测试" print(get_str())
- 打印结果:
这里是装饰器添加的功能 测试
三、带有参数的装饰器,是装饰器加参数,如下
def set_level(level): def set_func(func): def call_func(*args,**kwargs): if level ==1 : print("权限验证-- %d"%level) elif level ==2 : print("权限验证-- %d"%level) else: print("权限验证-- %d"%level) func(*args,**kwargs) return call_func return set_func @set_level(9) def test1(): print("------test1------") test1()
- 分析:上面就是给装饰器加参数
set_level(9)
:
- 1、调用set_level并且将9当做实参传递
- 2、用上一步调用的返回值当做装饰器对test1函数进行装饰
总结:不要觉得装饰器很难,其实它就是:函数的指向的改变,以闭包为例,闭包作为另一个函数的装饰器,装饰后,在闭包的内部函数再去调用函数,其实就是在函数执行之前先调用闭包内的代码。