Python 基础 - Day 4 Learning Note - Decorator 装饰器
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python 基础 - Day 4 Learning Note - Decorator 装饰器相关的知识,希望对你有一定的参考价值。
-
装饰器的知识准备
- 函数,函数参数
- 作用域: 全局变量,局部变量
- 变量解析规则: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("\033[32;1mUser has passed authorization\033[0m") 13 res = func(*args,**kwargs) # 赋值 14 return res # 返回res 15 else: 16 exit(‘\033[31;1mInvalid username or password\033[0m‘) 17 return wrapper # 返回wrapper的内存地址 18 19 @auth(auth_type="local") 20 def home(): 21 print(‘welcome to home page‘) 22 return "from home" 23 24 print(home()) #打印返回from home
- 带参数的装饰器(最终版装饰器):如果装饰器带参数,被装饰的函数有参数,那么装饰器将会有三层。
1 #!user/bin/env python 2 # -*- coding:utf-8 -*- 3 4 # 例三:带参数的装饰器(终结版装饰器) 5 # 情景要求: 配用多种认证方式。 home()认证方式用本地local认证,bbs()用远程的ldap 6 7 user,passwd = ‘alex‘,‘abc123‘ 8 9 10 def auth(auth_type): # home()的can 11 def outer_wrapper(func): 12 def wrapper(*args,**kwargs): 13 username = input(‘Username‘).strip() 14 password = input(‘Password‘).strip() 15 16 if user == username and passwd == password: 17 print("\033[32;1mUser has passed authorization\033[0m") 18 res = func(*args,**kwargs) # 赋值 19 return res # 返回res 20 else: 21 exit(‘\033[31;1mInvalid username or password\033[0m‘) 22 return wrapper # 返回wrapper的内存地址 23 return outer_wrapper 24 25 def index(): 26 print(‘welcome to index page‘) 27 28 29 @auth(auth_type="local") 30 def home(): 31 print(‘welcome to home page‘) 32 return "from home" 33 34 home() 35 36 @auth(auth_type="ldap") 37 def bbs(): 38 print(‘welcome to bbs page‘) 39 40 bbs()
Reference:
“Python 里为什么函数可以返回一个函数内部定义的函数?”, https://www.zhihu.com/question/25950466/answer/31731502
“12步轻松搞定python装饰器”,http://python.jobbole.com/81683/
以上是关于Python 基础 - Day 4 Learning Note - Decorator 装饰器的主要内容,如果未能解决你的问题,请参考以下文章