函数(三)——闭包函数与装饰器
一、
def index():pass res = index res()
2. 可以当做函数的参数
def index(): # ① print('from index') def func(a): # ② print(a) # ④ 执行func函数的子代码,a = index >>> <function index at 0x00000197298A73A0> a() # ⑤ a()相当于index(),调用index函数 func(index) # ③ 调用func函数
3. 可以当做函数的返回值
def index(): # ① print('from index') def func(): # ② print('from func') # ④ 执行func函数体代码 from func return index # ⑤ res = func() # ③右边调用func函数 ⑥左边接受index的返回值 res = index print(res) # res的函数体代码 <function index at 0x0000029815CF73A0> res() # ⑦ res()相当于index(),调用index函数 from index
def index(): # 1 print('from index') # 3.执行index函数体代码 from index def func(): # 4 print('from func') # 8. 执行func函数体代码 from func return func # 5. 返回func res = index() # 2.右边调用index函数 6. res接受func的返回值,res = func print(res) # 打印func的函数体代码 <function index.<locals>.func at 0x000002602F357700> res() # 7.res()相当于func(),调用func函数
4. 可以当做容器类型(可以存放多个数据的数据类型)的数据
例:要通过不同的编号实现注册、登录、提现、转账、购物等功能
方法1:
def register(): print('注册功能') def login(): print('登录功能') def withdraw(): print('提现功能') def transfer(): print('转账功能') def shopping(): print('购物功能') # while True: print(""" 1.注册功能 2.登录功能 3.提现功能 4.转账功能 5.购物功能 """) choice = input('>>>:').strip() if choice == '1': register() elif choice == '2': login() elif choice == '3': withdraw() elif choice == '4': transfer() elif choice == '5': shopping() else: print('其他')
该方法比较麻烦,有多少种功能就得有多少个选择分支。
方法2:
def register(): print('注册功能') def login(): print('登录功能') def withdraw(): print('提现功能') def transfer(): print('转账功能') def shopping(): print('购物功能') # 定义功能编号与功能的对应关系 func_dict = { '1': register, '2': login, '3': withdraw, '4': transfer, '5': shopping } while True: print(""" 1.注册功能 2.登录功能 3.提现功能 4.转账功能 5.购物功能 """) choice = input('>>>:').strip() if choice in func_dict: func_name = func_dict.get(choice) func_name() else: print('功能编号不存在')
把函数名放到一个字典里,通过输入的数值提取数值对应的键的值,即函数名,函数名加括号即可调用相应的函数。
二、闭包函数
什么是闭包函数:
1.定义在函数内部
2.用到外部函数名称空间中的名字
闭包函数实际应用
是另外一种给函数体代码传参的方式
给函数体代码传参的方式1
代码里面缺什么变量名形参里面就补什么变量名
def register(name,age): print(f""" 姓名:{name} 年龄:{age} """) register('jason', 18)
给函数体代码传参的方式2:闭包函数
def outer(name, age): # name = 'jason' # age = 18 def register(): print(f""" 姓名:{name} 年龄:{age} """) return register res = outer('jason', 18) res() # jason, 18 res() # jason, 18 res = outer('kevin', 28) res() # kevin, 28 res() # kevin, 28
三、
1.什么是装饰器:
装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。
2. 装饰器的原则:
1) 不修改被装饰对象的源代码
2) 不修改被装饰对象的调用方式
3.本质
并不是一门新的技术,而是由函数参数、名称空间、函数名多种用法、闭包函数组合到一起的结果,目的是为被装饰对象添加上新功能。
4.口诀
对修改封闭,对扩展开放
5.储备知识
时间相关操作
import time print(time.time()) # 时间戳(距离1970-01-01 00:00:00所经历的秒数) time.sleep(3) print('睡醒了 干饭')
count = 0 循环之前先获取时间戳 start_time = time.time() while count < 100: print('哈哈哈') count += 1 end_time = time.time() print('循环消耗的时间:', end_time - start_time)
四、装饰器推导:
被装饰者:
import time def index(): time.sleep(3) print('from index') index()
要统计index函数执行的时间:
1. 直接在调用index函数的前后添加代码
import time def index(): time.sleep(3) print('from index') '''1. 直接在调用index函数的前后添加代码''' start_time = time.time() index() end_time = time.time() print('函数的执行时间>>>:',end_time-start_time)
2. index调用的地方较多,代码不可能反复拷贝>>>:相同的代码需要在不同的位置反复执行>>>:函数
def index(): time.sleep(3) print('from index') def get_time(): start_time = time.time() index() end_time = time.time() print('函数的执行时间>>>:', end_time - start_time) get_time()
3.上述函数体代码写死了,只能统计index的执行时间,如何才能做到统计更多的函数运行时间>>>:直接传参变换统计的函数
def index(): time.sleep(3) print('from index') def home(): time.sleep(3) print('from home') def get_time(xxx): start_time = time.time() xxx() end_time = time.time() print('函数的执行时间>>>:', end_time - start_time) get_time(index) get_time(home)
4. 上述虽然实现了一定的兼容性,但是并不符合装饰器的特征
def index(): time.sleep(3) print('from index') def home(): time.sleep(3) print('from home') def outer(xxx): def get_time(): start_time = time.time() xxx() end_time = time.time() print('函数的执行时间>>>:', end_time - start_time) return get_time res = outer(index) res() res1 = outer(home) res1()
5. 调用方式还是不对,如何变形>>>:变量名赋值绑定
def index(): time.sleep(3) print('from index') def home(): time.sleep(3) print('from home') def outer(xxx): # xxx真正的index函数指向的index函数名绑定的内存地址 def get_time(): start_time = time.time() xxx() end_time = time.time() print('函数的执行时间>>>:', end_time - start_time) return get_time # 左边的变量名可以为任意的,所以我们也可以让它与要统计的函数名称一致,但是它接收的是get_time函数的内存地址 index = outer(index) # 括号里面为真正的index函数 index() home = outer(home) home()
6.上述装饰器只能装饰无参函数,兼容性性太差
def func(a): time.sleep(3) print('from func') def outer(xxx): # xxx真正的index函数指向的index函数名绑定的内存地址 def get_time(a): start_time = time.time() xxx(a) end_time = time.time() print('函数的执行时间>>>:', end_time - start_time) return get_time func = outer(func) func(1)
7. 被装饰的函数不知道有没有参数以及有几个参数,如何兼容
def func(a): time.sleep(3) print('from func') def func1(a,b): time.sleep(3) print('from func1') def outer(xxx): # xxx真正的index函数指向的index函数名绑定的内存地址 def get_time(*args,**kwargs): start_time = time.time() xxx(*args,**kwargs) end_time = time.time() print('函数的执行时间>>>:', end_time - start_time) return get_time func = outer(func) func(1) func1 = outer(func1) func1(1,2)
8.如果被装饰的函数有返回值
def func(a): time.sleep(3) print('from func') return 'func' def func1(a,b): time.sleep(3) print('from func') def outer(xxx): # xxx真正的index函数指向的index函数名绑定的内存地址 def get_time(*args,**kwargs): start_time = time.time() res = xxx(*args,**kwargs) end_time = time.time() print('函数的执行时间>>>:', end_time - start_time) return res return get_time func = outer(func) res = func(1) print(res) func1 = outer(func1) func1(1,2)
六、
多层语法糖,加载顺序由下往上
每次执行之后如果上面还有语法糖,则直接将返回值函数名传给上面的语法糖
如果上面没有语法糖了,则变形 index = outter1(wrapper2)
def outer1(func1): # 1 print('加载了outer1') # 14. 执行outer1,'加载了outer1' def wrapper1(*args, **kwargs): # 15 print('执行了wrapper1') # 18. 执行wrapper1的函数体代码,’执行了wrapper1‘ res1 = func1(*args, **kwargs) # 19. func1为outer1的参数,第13步中outer1接受了wrapper2的返回值,func1 = wrapper2,func1()相当于wrapper2(),调用wrapper2 return res1 return wrapper1 # 16. 返回wrapper1, 传递给index, index = wrapper1 def outer2(func2): # 2 print('加载了outer2') # 10. 执行outer2, '加载了outer2' def wrapper2(*args, **kwargs): # 11 print('执行了wrapper2') # 20. 执行wrapper2, '执行了wrapper2' res2 = func2(*args, **kwargs) # 21. func2为outer2的参数,第9步中outer2接受了返回的wrapper3,func2 = wrapper3, func2()相当于wrapper3(),调用wrapper3 return res2 return wrapper2 # 12. 返回wrapper2, 传递给语法糖上面的outer1 def outer3(func3): # 3 print('加载了outer3') # 6.执行outer3的函数体代码,‘加载了outer3' def wrapper3(*args, **kwargs): # 7 print('执行了wrapper3') # 22. 执行wrapper3, '执行了wrapper3' res3 = func3(*args, **kwargs) # 23. func3为outer3的参数,outer3接受了index, func3 = index, func()相当于index(), 调用index return res3 return wrapper3 # 8.返回wrapper3,传递给语法糖上面的outer2 @outer1 # 13. outer1接受wrapper2的返回值,outer1(wrapper2),调用outer1 @outer2 # 9. outer2接受返回的wrapper3, outer2(wrapper3),调用outer2 @outer3 # 5 index传递给outer, outer3(index),调用outer3 def index(): # 4 print('from index') # 24. 执行index的函数体代码,’from index‘ index() # 17. index()相当于wrapper1(),调用wrapper1函数
执行顺序:
加载了outer3
加载了outer2
加载了outer1
执行了wrapper1
执行了wrapper2
执行了wrapper3
from index
七、有参装饰器
当装饰器中需要额外的参数时,需要有参装饰器。
例:校验用户是否登录装饰器
def outer(mode): def login_outer(func_name): def inner(*args, **kwargs): username = input('username>>>:').strip() password = input('password>>>:').strip() if mode == '1': print('数据直接写死') elif mode == '2': print('数据来源于文本文件') elif mode == '3': print('数据来源于字典') elif mode == '4': print('数据来源于MySQL') return inner return login_outer
八、
最常用的无参装饰器
def outer(func_name): def inner(*args, **kwargs): res = func_name(*args, **kwargs) return res return inner @outer def index(): pass
2. 不常用的有参装饰器
def outer_plus(mode): def outer(func_name): def inner(*args, **kwargs): res = func_name(*args, **kwargs) return res return inner return outer @outer_plus('MySQL') def func(): pass
九、
修饰前:
def outer(func_name): def inner(*args, **kwargs): """我是inner 我擅长让人蒙蔽""" res = func_name(*args, **kwargs) return res return inner @outer def func(): """我是真正的func 我很强大 我很牛 我很聪明""" pass help(func) # func绑定的是inner,help找到的也是inner的相关内容 print(func) func()
from functools import wraps def outer(func_name): @wraps(func_name) # 仅仅是为了让装饰器的效果更加逼真 平时可以不写 def inner(*args, **kwargs): """我是inner 我擅长让人蒙蔽""" res = func_name(*args, **kwargs) return res return inner @outer def func(): """我是真正的func 我很强大 我很牛 我很聪明""" pass help(func) # help查到的是func的内容 print(func) func()