装饰器
装饰器本质是函数,是用来装饰其他函数,顾名思义就是,为其他的函数添加附件功能的。
一、装饰器原则:
- 不能修改被装饰函数的源代码
- 不能修改被装饰函数的调用方式
def logging(): print("logging...") #正确写法,没有修改源码 def test1(): pass #错误写法,不能修改源码 def test1(): pass logging() # 调用方式,也不能被修改 test1()
二、装饰器知识:
- 函数即"变量"
- 高阶函数+嵌套函数 =》装饰器
1、函数即”变量“
python的内存机制,看如下代码:
#变量 x = 1 #函数 def test(): pass
在内存图中是这样表示的:
x、test 是变量名,保存在栈内存中,1、函数体 保存在堆内存中
2、高阶函数+嵌套函数 =》装饰器
装饰器实现过程:
第一步:原始代码
def home(): print("---首页----") def TV(): print("----TV----") def music() print("---music-----")
第二步:想给部分模块加个登陆认证
user_status = False #用户登录了就把这个改成True def login(): _username = "ABC" #假装这是DB里存的用户信息 _password = "12345" #假装这是DB里存的用户信息 global user_status if user_status == False: username = input("user:") password = input("pasword:") if username == _username and password == _password: print("welcome login....") user_status = True else: print("wrong username or password!") else: print("用户已登录,验证通过...") def home(): print("---首页----") def TV(): login() #执行前加上验证 print("----TV----") def music(): print("----music----")
虽然这样实现了认证功能,但是修改了被装饰函数的源代码,违背了装饰器的原则”不能修改被装饰函数的源代码“
第三步:代码改进,使用高阶函数理念,把函数名当参数传递给认证函数login,这样可以不修改被装饰函数源代码的情况下完成登陆认证
user_status = False #用户登录了就把这个改成True def login(func): _username = "ABC" #假装这是DB里存的用户信息 _password = "12345" #假装这是DB里存的用户信息 global user_status if user_status == False: username = input("user:") password = input("pasword:") if username == _username and password == _password: print("welcome login....") user_status = True else: print("wrong username or password!") if user_status == True: print("用户已登录,验证通过...") func() #只要验证通过了,就调用相应功能 def home(): print("---首页----") def TV(): print("----TV----") def music(): print("----music----") login(TV) #需要验证就调用 login,把需要验证的功能 当做一个参数传给login
虽然这样可以不修改被装饰函数源代码的情况下完成登陆认证,但是违背了装饰器原则”修改了被装饰函数的调用方式“,本来被装饰函数只需要TV()就可调用,现在变成了login(TV)
第四步:代码改进,使用匿名函数理念,将login(TV)变成 TV = login(TV) ,将函数当成值,赋值给变量名TV,跟关键字def 重新定义了TV是一样的效果,不过这样还有一个问题, TV = login(TV)这个赋值过程中,就把
函数TV给调用了,用户自己还没有调用,就自己自动调用肯定是不对的,这个时候需要用到嵌套函数的理念了,在认证函数login里面的再定义一个新函数login_inner,在login函数return(返回)login_inner函数名(对是return login_inner, 不是return login_inner(), 因为return 函数名 返回的是函数在栈内容的内存地址,return 函数名+() 返回的是该函数的执行结果) 这样在TV = login(TV)赋值的时候,TV赋值的就不是 login(TV)的执行结果了,赋值的值是login_inner的内存地址,等用户再调用的时候 就是TV(),这样就没有改变被装饰函数的调用方式了。
装饰器装饰没有参数函数:
import time #定义装饰器函数 def timmer(func): # 把test1这个函数名作为参数传递进来 func=test1 #定义装饰器中的内置函数 def deco(): start_time = time.time() func() #相当于运行test1() stop_time = time.time() print("the func run time is %s"%(stop_time-start_time)) return deco #装饰test1函数 @timmer # 相当于test1 = timmer(test1) def test1(): time.sleep(3) print("in the test1") #直接执行test1函数 test1() #输出 in the test1 the func run time is 3.0002999305725098