装饰器的知识准备
- 函数,函数参数
- 作用域: 全局变量,局部变量
- 变量解析规则:LEGB法则 - 假设嵌套函数(第二层函数),解析器查找内部函数的变量的顺序如下。 在任何一层先找到了符合要求的变量,则不再向外查找。如果没有,则抛出N
- Local - 本地函数内部,通过任何方式赋值的,而且没有被global关键字声明为全局变量的变量
- Enclosing - 直接该内部函数的外围空间(即它的上层函数)的本地作用域。多层嵌套,则有内而外逐层查找,直至最外层的函数
- Global - 全局空间(模块enclosed.py), 在顶层赋值的变量
- Buildin - 内置模块(__buildin__) 中预定义的变量名中查找变量。
- 变量生存周期:局部变量的生存周期随着函数的调用而存在,随着函数的结束而消亡。
- 嵌套函数:函数中套函数并调用。
- 高阶函数: 一个函数接受另一函数作为变量。 函数即变量
- Python中一切皆对象(objects,之后会讲到面对对象编程的问题)。 当定义一个函数时,函数第一类对象/一级类对象。 所谓第一类对象,意思就是可以用标识符对对象命名,并且对象可以当作数据处理。例如赋值,作为参数传递给函数,或者作为返回值return等。
- 函数对象 vs. 函数调用 (非常容易搞混)
def func():
return "hello,world"
ref1 = func # 将函数对象赋值给ref1 , type(ref1)是 function
ref2 = func() # 函数调用, type(ref2) 是 string
- 闭包: 装饰器其实就是一个闭包,把一个函数当做参数然后返回一个替代版函数。所以理解闭包概念很重要。 所谓闭包,就是将组成函数的语句和这些语句的执行环境打包在一起时,得到的对象。总结:
- 闭包最重要的使用价值:封存函数执行的上下文环境;
- 闭包在其捕捉的执行环境(def语句快所在上下文)中,也遵循LEGB规则逐层查找,直至符合要求的变量,或者抛出异常
装饰器
概念:
定义:装饰器的“器“就是函数,基本语法用def 来定义,本质就是函数,其功能是装饰其他函数,为其他函数添加附加功能
原则:装饰器对被装饰函数完全透明
- 甭能修改被装饰函数的源代码
- 不能修改被装饰函数的调用方式
结构:高阶函数 + 嵌套函数;
- 函数 即“变量“
- 高阶函数嵌套函数
- 把一个函数名当作实参传入另一个函数 ------ 在其不修改被装饰函数源代码的情况下为其添加功能
- 返回值中包含函数名 ----- 不修改函数的调用方式
总结下, 装饰器就是一个返回值为函数的高阶函数,其中至少嵌套一个函数(作为返回值返回)
工作原理:Func = Deco(Func) 用语法糖 syntax suger来表示@
第一步: 被装饰的函数作为参数传递给装饰器函数,并执行装饰器函数, 返回值记作Newfunc
第二步: 原函数重新赋值为Newfunc
课堂案例 - 如何构建装饰器
- 最简单装饰器: Moduel 2 - Vedio 7 - 装饰器的小高潮。
1 #!user/bin/env python
2 # -*- coding:utf-8 -*-
3
4 import time
5
6
7 def timer(func): # timer(test1), 将test1的内存地址传给func; 其实最终通过return返回deco的内存地址
8 def deco():
9 start_time = time.time()
10 func()
11 stop_time = time.time()
12 print('the func run time is %s' %(stop_time-start_time))
13 return deco # 直接返回deco函数的内存地址,如此下面test1 = deco(test1),如此test1()就可以正常调用
14
15
16 @timer # 就是一步运行工作,等于test1 = timer(test1) 注意此处不能加括号,因为装饰的函数,而test1()是一个返回值
17 def test1():
18 time.sleep(3)
19 print('in the test1')
20
21
22 test1() # 此时test1 执行的是deco的内存地址,因为timer函数中的return deco
23 print(test1) # 被装饰过的test1的内存地址返回: <function timer.<locals>.deco at 0x00000244D3824048>
逻辑解释
起始行 | 结束行 | 代码 | 解释 |
- | 4 | import time | 导入time模块 |
4 | 7 | def timer(func) | timer(test1), 将test1的内存地址闯入func, 最终return deco 的内存地址 |
7 | 16 | @timer |
直接跳到语法糖,执行timer装饰器 表示: test1 = timer(test1) |
16 | 8 | def deco() | |
8 | 13 | return deco | 返回值是deco的内存地址 |
13 | 22 | test1() | 调用test1(),经过@timer的重新赋值,此时执行的是deco的内存地址,最终调用deco() |
22 | 9 | start_time = time.time() | 执行deco() |
9 | 10 | func() | 在deco函数内部,开始调用变量func, 此时,func即test1(), |
10 | 18 | time.sleep(3) | 执行原test1() |
18 | 19 | print('in the test1) | |
19 | 11 | stop_time = time.time() | |
11 | 12 | print('run time = %s' %(stop_time - start_time) |
核心是内存地址的转移。
- 被装饰的函数带参数:
#!user/bin/env python
# -*- coding:utf-8 -*-
import time
def timer(func): # 将test2的内存地址传给func
def deco(*args,**kwargs): # 用非固定参数*args, **kwargs;如此满足有参数,和没有参数的被装饰函数
start_time = time.time()
func(*args,**kwargs)
stop_time = time.time()
print('the func run time is %s' %(stop_time-start_time))
return deco # 直接返回deco函数的内存地址
@timer # test2 = timer(test2)= deco; test2() = deco(); 所以当test2有函数变量,deco也需要加函数变量。
def test2(name): # test2本身带参数
time.sleep(3)
print('in the test2')
test2('alex')
在这种情况下,
- 被装饰函数test2自带参数的情况下, 其对应装饰器中最终换回的内存地址的函数deco()也应带有函数。
- 因为装饰器最终要用于不同的被装饰函数,对于deco()的参数应用 非固定参数 *args 和 **kwargs。
- 被装饰的函数有返回值:由于home() = wrapper(home),最终装饰过的home()返回为wrapper的内存地址的调用。 原函数的return数据也要在wrapper中写入。
1 #!user/bin/env python
2 # -*- coding:utf-8 -*-
3
4 user,passwd = 'alex','abc123'
5
6 def auth(func):
7 def wrapper(*args,**kwargs):
8 username = input('Username').strip()
9 password = input('Password').strip()
10
11 if user == username and passwd == password:
12 print("