ql的python学习之路-day9

时间:2021-07-26 21:54:59

前言:本节主要学习装饰器

一、装饰器

定义:本质上是个函数,用来装饰其他函数;(就是为其他函数添加附加功能)

原则: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)时候就是调用被装饰的函数