前言:本节主要学习装饰器
一、装饰器
定义:本质上是个函数,用来装饰其他函数;(就是为其他函数添加附加功能)
原则:1.不能修改被装饰的函数的源代码
2.不能修改被装饰的函数的调用方式
以上两点可以总结出装饰器对被装饰的函数来说是完全透明的,因为装饰器不能修改原函数的源代码,被装饰的函数还是像往常一样继续运行。
实现装饰器的知识储备:1.函数即‘变量’
2.高阶函数
3.嵌套函数
可以理解成:高阶函数 + 嵌套函数 = 装饰器
1.函数即‘变量’
内存回收机制:数据存储在内存中,用变量名来引用;如果把内存比作一栋大厦,数据比作一个房间,变量名相当于门牌号,用门牌号来访问房间;如果删除了变量名,在一段时间内没有引用数据,python的内存回收机制就会回收数据。
函数如同变量:函数和变量一样也有内存回收机制,在定义函数的时候相当于把函数体赋值给了函数名,在内存里面存储着函数体,然后用函数名来引用;匿名函数也是一样,在内存中存储lambda表达式,如果把lambda表达式赋值给一个变量名,就能用变量来引用lambda表达式。
2.高阶函数
满足下面任意一条就可以看做是高阶函数:
a:把一个函数名当做实参传给另外一个函数(在不修改被装饰函数源代码的情况下为其添加功能)
b:返回值中包含函数名(不修改函数的调用方式)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author:qinjiaxi 4 import time 5 6 def bar(): 7 time.sleep(3) 8 print('in the test1') 9 10 def test1(func): 11 start_time = time.time() 12 func() 13 stop_time = time.time() 14 print("time func run time is %s" % (stop_time-start_time)) 15 16 test1(bar)
3.嵌套函数
在一个函数体内定义一个函数,而不是在一个函数里面调用函数,这个叫做函数的嵌套。
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author:qinjiaxi 4 x = 2 5 def grandpa(): 6 x = 1 7 def father(): 8 x = 2 9 def son(): 10 x = 3 11 print(x) 12 son() 13 father() 14 grandpa()
返回值是3
4.装饰器实例
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author:qinjiaxi 4 import time 5 #timer是装饰器满足嵌套函数(函数里面定义一个函数),满足高阶函数(返回值有函数名) 6 def timer(func): 7 def deco(*args, **kwargs): 8 start_time = time.time() 9 func(*args, **kwargs)#run test1 10 stop_time = time.time() 11 print("the func run time is %s" % (stop_time - start_time)) 12 return deco 13 @timer #test1 = timer(test1)#调用装饰器 14 def test1(): 15 time.sleep(3) 16 print("in the test1") 17 test1()#这个不是上面这个test1函数,其实是运行deco这个内存地址
5.装饰器实例升级版
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author:qinjiaxi 4 import time 5 user, password = 'qinjiaxi', 'abc123' 6 def auth(auth_type): 7 print("auth func", auth_type) 8 def out_wrapper(func): 9 def wrapper(*args, **kwargs): 10 print("wrapper func args", *args, **kwargs) 11 if auth_type == "local": 12 username = input("Username:").strip() 13 passwd = input("Password:").strip() 14 if user == username and password == password: 15 print("\033[32;1mUser has passed authentication\033[0m") 16 res = func(*args,**kwargs)#from home 17 print("-----after authentication-----") 18 return res 19 else: 20 exit("\033[31;1mInvalid username or password\033[0m") 21 elif auth_type == 'ldap': 22 print("sorry I do not know") 23 return wrapper 24 return out_wrapper 25 26 27 def index(): 28 print("welcome to index page") 29 30 @auth(auth_type='local')#home = wrapper /home = out_wrapper(home) 31 def home(): 32 print("welcome to home page") 33 return "from home" 34 35 @auth(auth_type="ldap")#bbs = wrapper 36 def bbs(): 37 print("welcome to bbs page") 38 39 index() 40 print(home()) 41 bbs()
注:装饰器对于初学者来说还是比较难以理解的。实际多debug一下就会理解其中的原理,下面简单的分几个步骤剖析一下(拿上面的升级版实例来说明):
1.当调用装饰器时(实例中的@auth(auth_type = ‘xxxx’))其实程序运行时先找到调用装饰器的位置,从装饰器里面一步一步的return出最里层的函数的内存地址,也就是函数名(实例中的wrapper)
2.返回出最里层函数名后跳到最后面的调用函数部分(实例中最后三行)来调用函数(这个函数其实就是上面最里层的函数)
3.然后执行装饰器最里层的函数,执行到res = func(*args, **kwargs)时候就是调用被装饰的函数