装饰器
Posted todo000
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了装饰器相关的知识,希望对你有一定的参考价值。
目录:
函数装饰器
类装饰器
函数装饰器:
1.装饰器本质是函数,是用来为其他函数增加功能的函数
2,装饰器需要遵循的原则:
1.不改变被装饰函数的调用方式
2.不改变被装饰函数的源代码
3.实现装饰器需要
1.闭包
闭包就是引用了自有变量的函数,这个函数保存了执行的上下文,可以脱离原本的作用域独立存在.
2.高阶函数
接收其他函数作为参数,视为高阶函数
3.嵌套函数
函数中套函数
装饰器的实现推导:
使用高阶函数,函数的参数可以接收函数名(函数的内存地址)
import time def timer(func): #装饰器模块 start_time = time.time() func() print("用时",time.time() - start_time) def func(): #被装饰模块 print(‘我是测试‘) time.sleep(0.5) print("测试结束") timer(func) #func函数确实加上了测时功能,但是改变了调用方式 # 我是测试 # 测试结束 # 用时 0.500999927520752
使用嵌套函数
def timer(): start_time = time.time() def inner(): #在函数体内嵌套一层函数,叫做嵌套函数 print(start_time) time.sleep(0.5) print(time.time()) print("end_time",time.time() - start_time) inner() timer() # 1569502444.306 # 1569502444.806 # end_time 0.5
使用闭包
def timer(name): def inner(age): print("姓名: %s 年龄:%s" %(name,age)) #引用了外部的name变量 return inner #返回的内部函数的未执行状态 inner = timer("alex") inner(25) # 姓名: alex 年龄:25 # 当timer函数被引用时,timer函数已经执行完毕,通常情况下,其拥有的name参数应该被回收 # 但因为其内部的inner函数调用了name变量,使得name变量成为了自由变量 # 因为timer的执行结果返回的是inner的未执行状态,当我们再执行inner时,name的变量得到引用
我们结合使用高阶函数,嵌套函数,闭包概念
就可以实现装饰器的功能
就可以在不改变调用方式的情况下,给函数添加上其他功能
但这不是装饰器的标准写法
def wrapper(func): def inner(): print("我是装饰器") func() print("装饰结束") return inner def index(): print("我是index") index = wrapper(index) index() # 我是装饰器 # 我是index # 装饰结束
正确的写法:
def wrapper(func): def inner(): print("我是装饰器") func() print("装饰结束") return inner @wrapper #@wrapper 相当于 index = wrapper(index) def index(): print("我是index") index() # 我是装饰器 # 我是index # 装饰结束
我们实现了简单的装饰器
但这样的被装饰函数没有传参,再给被装饰函数加上传参功能
def wrapper(func): def inner(*args,**kwargs): print("我是装饰器") res = func(*args,**kwargs) print("装饰结束") return res #为了获得index()的执行返回值,我们将func()的执行结果返回去,因为func被传入的参数是index,所以得到的就是index()的返回值 return inner @wrapper def index(name): print("%s的index" %name) index("alex") # 我是装饰器 # alex的index # 装饰结束
目前已经可以解决实际应用场景中的绝大部分问题
在此基础上
我们还可给装饰加上传参功能
def wrapper(type): def outer(func): def inner(*args,**kwargs): print("我是装饰器") print(type) res = func(*args,**kwargs) print("装饰结束") return res return inner return outer @wrapper(‘index‘) def index(name): print("%s的index" %name) @wrapper("home") def home(name): print("%s的home" %name) index(‘alex‘) home("todo") # 我是装饰器 # index 装饰器传入的参数 # alex的index 被装饰函数的参数 # 装饰结束 # 我是装饰器 # home # todo的home # 装饰结束
装饰器实际应用的两个练习
1.给函数加上当前时间功能
import datetime def wrapper(func): def inner(*args,**kwargs): res = func(*args,**kwargs) print(datetime.datetime.now()) return res return inner @wrapper def index(name): print("我是%s的index" %name) @wrapper def home(name): print("我是%s的home" %name) index("alex") home("todo") # 我是alex的index # 2019-09-26 22:08:49.297000 # 我是todo的home # 2019-09-26 22:08:49.297000
2.给每个网页加上验证功能,需要多种验证方式
import datetime status = [False] user = "aaa" pwd = "123" def wrapper(type): def outer(func): def inner(*args,**kwargs): if type == ‘local‘ and status[0] == False: count = 3 while count > 0: username = input("输入用户名: ").strip() password = input("输入密码: ").strip() if user == username and pwd == password: res = func(*args,**kwargs) status[0] = True print("验证通过",datetime.datetime.now()) return res else: count -= 1 print("请重新输入") else: print("验证失败") elif type == "ldap" and status[0] == False: print("验证ldap...") res = func(*args,**kwargs) return res elif status[0] == True: print("验证通过") res = func(*args,**kwargs) return res return inner return outer @wrapper("local") def index(name): print("我是%s的index" %name) @wrapper(‘local‘) def home(name): print("我是%s的home" %name) @wrapper(‘ldap‘) def bbs(name): print("我是%s的bbs" %name) index("alex") home("todo") bbs("yi")
使用装饰器可以极大的复用函数,但也会产生一个问题,装饰器在给一个函数装饰后,会覆盖其原函数的元信息,当函数被反射调用时,会出现问题
def outer(func): def inner(*args,**kwargs): print("我是装饰器") res = func(*args,**kwargs) print("装饰结束") return res return inner @outer def index(name): print("我是 %s 的 Index" %name) print(index.__name__) index("alex") # 我是装饰器 # 我是 alex 的 Index # inner #元信息被覆盖,当函数被用到反射调用时,会出现问题 # 装饰结束
使用functools.wraps方法,解决元信息被覆盖问题
import functools def outer(func): @functools.wraps(func) def inner(*args,**kwargs): print("我是装饰器") res = func(*args,**kwargs) print("装饰结束") return res return inner @outer def index(name): print("我是 %s 的 Index" %name) print(index.__name__) # 我是装饰器 # 我是 alex 的 Index # index #返回原函数的元信息 # 装饰结束
类装饰器
类方法实现装饰器
import functools class Wrapper(object): def __init__(self,name): self.name = name #装饰器参数 def __call__(self,func): @functools.wraps(func) def inner(*args,**kwargs): self.info() #可以将功能单独实现,这样函数更规范优雅 res = func(*args,**kwargs) self.end("Yes") return res return inner def info(self): print("装饰器参数 %s" %self.name) def end(self,flag): print("装饰结束了吗: %s" %flag) class Web(): def __init__(self,name): self.name = name @Wrapper("wrap") #传入装饰器参数,实现装饰功能,类似的@property功能也是这样实现的 def index(self,index_name): print("这个是%s网站上的%s的网页" %(self.name,index_name)) print(self.index.__name__) #这个函数的元信息 w = Web(‘alex‘) w.index("taobao") # 装饰器参数 wrap # 这个是alex网站上的taobao的网页 # index # 装饰结束了吗: Yes
以上是关于装饰器的主要内容,如果未能解决你的问题,请参考以下文章