Table of Contents
01介绍
不会装饰器,不能说会python哟

def foo(): print('foo')
foo#函数名指向函数 foo()#执行函数
foo=lambda x: x+1#foo变量名被指向匿名函数 foo()#执行lambda表达式 |
Py把函数名、方法名、变量名都当作变量名对待,所以不能相同
只要有相同的,则用的时候就是最后定义的一个
需求:基础平台部门写功能,业务部门调用(容易被裁员)
让业务部门调用的时候加权限验证,不加不能调用功能函数
#法1:冗余过多 def f1(): #权限验证 print('f1') def f2(): #权限验证 print('f2')
f1() f2() |
#法2 def check_login(): #权限验证 pass
def f1(): check_login() print('f1') def f2(): check_login() print('f2')
f1() f2() |
开放封闭原则规定已经实现的功能代码不能修改(封闭),但可以扩展(开放)
装饰器效果
def set_func(func): def call_func(): print("权限验证") func() return call_func
@set_func#在函数执行前加代码 def test1(): #set_func()#类似效果 print("test1")
test1() |
02实现装饰器
装饰器实现过程:
def set_func(func):#传入的指针是test1 def call_func(): print("权限验证") func() return call_func
def test1(): print("test1")
ret=set_func(test1)#ret得到call_func指针 ret()#执行print("权限验证")和调用func()即指向test1 |
test1=set_func(test1)#不用修改调用的函数名,重新修改指针 test1()#执行print("权限验证")和func()即test1 |
@set_func可看作一种简略写法
@set_func#等价于test1=set_func(test1) def test1(): print("test1") |
由于装饰器(修饰器)的闭包实现过程是整个调用原函数,新加的功能只能在原函数调用之前或之后执行,不能在执行到一半时插入
03装饰器的作用 对有参函数的装饰
装饰器能够实现对功能的扩展,同时不改变调用的函数名
作用:如计算函数运行的时间
Import time time.time() |
1508763827.814883 |
整数部分是1970年到现在秒数(Unix时间戳)
import time def set_func(func):#传入的指针是test1 def call_func(): start_time=time.time() func() stop_time=time.time() print("all time is %f" % (stop_time - start_time)) return call_func
@set_func#等价于test1=set_func(test1) def test1(): print("test1") test1()#执行print("权限验证")和func()即test1 |
对无参数、返回值函数最简单
对有参数无返回值函数装饰
def set_func(func):#传入的指针是test1 def call_func(a):#2.接收到100 print("权限验证") func(a)#3.100作为实参调用test1 return call_func
@set_func def test1(num): print("test1:%d" % num)
test1(100)#1.100给了call_fun |
04 装饰多个函数
同一个装饰器装饰多个函数
def set_func(func): def call_func(a): print("权限验证") func(a) return call_func
@set_func def test1(num): print("test1:%d" % num)
@set_func def test2(num): print("test2:%d" % num)
test1(100)#1.100给了call_fun test2(200) |
两次装饰分别创建多个闭包
test1、test2分别指向另一个函数,该函数分别嵌套原本的func(test1、test2)
装饰器@写完就已经装饰了,而不需要等调用test1()
def set_func(func): print("开始装饰") def call_func(a): print("权限验证") func(a) return call_func
@set_func def test1(num): print("test1:%d" % num)
#test1(100) |
test1未运行 终端打印出:开始装饰 |
05不定长参数的函数装饰
不定长参数的即便使用:
def test1(num,*args,**kwargs): print("test1:%d" % num) print("test1:", args)#作为元组打印 print("test1:", kwargs)#作为元组打印
test1(1,2,3,mm=1) |
其中mm=1为关键字参数
输出结果:
test1:1 test1:(2,3) test1:{‘mm’:1} |
使用解释器:
def set_func(func): print("开始装饰") def call_func(*args,**kwargs):#num1在args中以元组保存 print("权限验证") func(*args,**kwargs)#传递处也要带*,*代表对元组拆包,**代表对字典拆 return call_func
@set_func def test1(num,*args,**kwargs):#*代表多余的普通参数给args,**代表关键字参数给kwargs print("test1:%d" % num) print("test1:", args) print("test1:", kwargs)
test1(1,2,3,mm=1)#调用call_func |
传递可变参数时不能写成func(args,kwargs)否则只传递了一个元组一个字典
06 返回值函数装饰 通用装饰器
def set_func(func): print("开始装饰") def call_func(*args,**kwargs):#num1在args中以元组保存 print("权限验证") return func(*args,**kwargs)#将接收到的返回值传递 return call_func
@set_func def test1(num,*args,**kwargs):#*代表多余的普通参数给args,**代表关键字参数给kwargs print("test1:%d" % num) print("test1:", args) print("test1:", kwargs) return "ok"#谁调用的test1就向谁返回
ret=test1(1)#调用call_func print(ret) |
无返回值函数可适用:
一般接收无返回值函数,拿到的都是none
使用该装饰器装饰无返回值的函数,return none结果也一样是none
多返回值可适用:
多返回值时返回元组
def set_func(func): print("开始装饰") def call_func(*args,**kwargs): print("权限验证") return func(*args,**kwargs) return call_func
@set_func def test1(num,*args,**kwargs): print("test1:%d" % num) print("test1:", args) print("test1:", kwargs) return "ok","2"#多返回值时返回元组
ret=test1(1)#调用call_func print(ret) |
通用装饰器主要有3点:
- call_func(*args,**kwargs)接收可变参数
- func(*args,**kwargs)传递实参时拆包
- return func将接收到的返回值传递
07多个装饰器对同一个函数装饰
def add_verify1(func): print("开始1装饰") def call_func(*args,**kwargs):#num1在args中以元组保存 print("装饰1") return func(*args,**kwargs)#将接收到的返回值传递 return call_func def add_verify2(func): print("开始2装饰") def call_func(*args,**kwargs):#num1在args中以元组保存 print("装饰2") return func(*args,**kwargs)#将接收到的返回值传递 return call_func
@add_verify1 @add_verify2 def test1(): print("test1")
test1() |
输出结果
开始2装饰 开始1装饰 装饰1 装饰2 |
装饰与执行顺序不同
装饰add_verify1时,将add_verify2包括以下当成整个函数,需要先装饰下面的,下面的就变成新闭包
@add_verify1#等价于test1=add_verify1(test1),此时的test1指向已经改变 @add_verify2#等价于test1=add_verify2(test1) def test1(): print("test1") |
08应用
对原函数返回值加"<h1></h1>"标签
def set_func_1(func): def call_func(*args,**kwargs): return "<h1>"+func(*args,**kwargs)+"</h1>" return call_func
@set_func_1 def get_str(): return "haha"
print(get_str()) |
再加"<td></td>"标签
def set_func_1(func): def call_func(*args,**kwargs): # print("装饰1") return "<h1>"+func(*args,**kwargs)+"</h1>" return call_func def set_func_2(func): def call_func(*args,**kwargs): # print("装饰1") return "<td>"+func(*args,**kwargs)+"</td>" return call_func
@set_func_1 @set_func_2 def get_str(): return "haha"
print(get_str()) |
输出结果:
<h1><td>haha</td></h1> |
因为h1装饰的在最上面,执行的时候再递归调用td的装饰
09类装饰器
用类对函数装饰
class Test(object): def __init__(self,func):#带self的可算作实例方法,但肯定不是类方法 self.func=func#存下func引用 def __call__(self): print("装饰器功能") return self.func()#指向get_str
@Test#get_str=Test(get_str)创建Test类的实例对象 def get_str(): return "haha"
print(get_str())#调用时调用的是类装饰器的call方法 |
若要改成通用装饰器:get_str()调用类的call方法,所以参数也在call接收
类方法,静态方法可以通过类名调用,如@Test.jingtaifangfa