python基础之闭包函数与装饰器
一、global与nonlocal
| money = 66 |
| |
| |
| def index(): |
| money = 123 |
| |
| |
| index() |
| print(money) |
| 1.global 局部名称空间直接修改全局名称空间中的数据(不可变类型:整型、字符串和元组) |
| |
| |
| money = 66 |
| |
| def index(): |
| global money |
| money = 123 |
| |
| index() |
| print(money) |
| |
| |
| money = 66 |
| l1 = [11,22,33] |
| def index(): |
| l1.append(44) |
| |
| index() |
| print(l1) |
只有赋值语法才涉及 名称存放 和 在 名称空间查找名字 的问题
| 2.nonlocal 内层局部名称空间 修改 外层局部名称空间中的数据 |
| |
| |
| def index(): |
| name = 'jason' |
| def inner(): |
| name = 'tom' |
| inner() |
| print(name) |
| index() |
| |
| |
| def index(): |
| name = 'jason' |
| def inner(): |
| nonlocal name |
| name = 'tom' |
| inner() |
| print(name) |
| index() |
二、函数名的多种用法
函数可以被当作“数据”来处理,此时可以将函数名看作是变量名
| 函数名其实绑定的也是一块内存地址,只不过改地址里面存放的不是数据值,而是一段代码,函数名加括号可以去调用这段代码 |
| |
| |
| def index(): |
| print('from index') |
| print(index) |
1.可以当变量名
| 1.可以当作变量名赋值 |
| |
| def index(): |
| print('from index') |
| res = index |
| res() |
2.可以当函数的参数
| 2.可以当作函数的参数传值进去 |
| |
| def index(): |
| print('from index') |
| def func(a): |
| print(a) |
| a() |
| func(index) |
print(函数名) 为打印 该函数的内存地址
print(函数名()) 为打印该函数的返回值
3.可以当函数的返回值
| 3.可以当作函数的返回值 |
| 1)返回值是数据值时,直接返回 |
| |
| def index(): |
| print('from index') |
| |
| def func(): |
| print('from func') |
| return 123 |
| |
| res = func() |
| print(res) |
| |
| 2)返回值是函数名时,赋值于其他变量名可以利用该变量名调用函数 |
| ( 1 )两个函数套娃 |
| |
| def index(): |
| print('from index') |
| |
| def func(): |
| print('from func') |
| return index |
| |
| res = func() |
| print(res) |
| res() |
| ( 2 )函数嵌套 |
| |
| def index(): |
| print('from index') |
| def func(): |
| print('from func') |
| return func |
| |
| res = index() |
| print(res) |
| res() |
函数嵌套时,名称空间的绑定关系

| 4.可以当作容器类型(可以存放多个数据的数据类型:列表、元组、字典、集合)的数据 |
| |
| def index(): |
| print('from index') |
| |
| l1 = [11, 22, 11.11, 'jason', index] |
| l1[-1]() |
| |
| |
| |
| 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_dict.get(choice)() |
| else: |
| print('功能编号不存在') |
三、闭包函数
| 闭包函数:定义在函数内部的函数,并且用到了外部函数空间的名字,闭函数是内部的函数,包函数是外部的函数 |
| 也就是函数被当作数据处理的时候,始终以自带的作用域为准,若内嵌函数包含外对外部函数作用域中变量的引用,那么该'内嵌函数'就是闭包函数 |
| ''' 满足的要求: |
| 1.该函数定义在函数内部 |
| 2.用到了外部函数名称空间的名字(变量名)''' |
| |
| def index(): |
| print('jason') |
| def inner(): |
| print(name) |
闭包函数的实际应用
1.传参方式:将值包给函数
| 1.闭包函数是另一种给函数体代码传参的方式,就是将该值包给函数 |
| |
| |
| |
| def register(name, age): |
| print(f''' |
| name:{name} |
| age:{age} |
| ''') |
| |
| |
| def outer(): |
| name = 'jason' |
| age = 12 |
| def register(name, age): |
| print(f''' |
| name:{name} |
| age:{age} |
| ''') |
| return register |
| res = outer() |
| |
1.闭包函数中名称空间与代码体的绑定关系
2.闭包函数中代码运行的顺序
当第5步的x 没有被使用时,会变灰,即pycharm提示当前程序内并没有使用到这个对象,只要在程序内没有其他代码使用它的时候就会变灰来表示没有被引用可以删除不占用资源
2.绑定一次数据值可以重复使用
| |
| def outer(name, age): |
| def register(name, age): |
| print(f''' |
| name:{name} |
| age:{age} |
| ''') |
| return register |
| res = outer('jason', 18) |
| res() |
| res() |
| res = outer('kevin', 38) |
| res() |
| res() |
| res() |
1.在1次传参后,可以重复使用
2.可以通过改变传进去的参数,对结果进行更改
四、装饰器
1.装饰器简介
| 1.概念 |
| 在不改变被装饰对象原代码和调用方式的情况下,给被装饰对象添加新的功能 |
| """ |
| 软件包含功能的源代码以及调用方式,都应该避免修改,避免出错。 |
| 而对于上线后的软件,新需求则意味着扩展的可能性,应该为程序提供扩展的可能性 |
| """ |
| 2.本质 |
| 并不是一门新的技术,而是由函数参数、名称空间、函数名多种用法、闭包函数组合到一起的结果 |
| 3.装饰器口诀 |
| 对修改封闭,对扩展开放 |
| """ |
| 软件的设计应该遵循开放封闭原则,对扩展开放,对修改封闭。对扩展开放意味着有新的需求或者变化时,可以对现有代码进行扩展,以适应新的情况。对修改封闭,意味着对象一旦设计完成,就可以独立完成其工作,而不要对其进行修改。 |
| """ |
| 4.储备知识 |
| 时间相关操作 |
| 1)时间戳 time.time |
| import time |
| print(time.time()) |
| 2)时间阻塞 time.sleep() |
| import time |
| time.sleep() |
2.装饰器推导流程
| |
| import time |
| def index(): |
| time.sleep(3) |
| print('from index') |
| def home(): |
| time.sleep(1) |
| print('from home') |
如果想为案例中的代码添加新的功能,该如何实现?
1.直接在调用index函数前后添加新功能的代码
| import time |
| def index(): |
| time.sleep(3) |
| print('from index') |
| def home(): |
| time.sleep(1) |
| print('from home') |
| |
| start_time = time.time() |
| index() |
| end_time = time.time() |
| print('函数index的执行时间为:', end_time-start_time) |
如果想要多次调用index函数如何实现新功能,该如何实现?
2. 定义新功能的函数实现多次调用index函数新功能
| 相同的代码不同位置反复执行==>>> 函数 |
| 定义新功能为函数,即可通过调用函数多次使用新功能 |
| |
| def get_time(): |
| start_time = time.time() |
| index() |
| end_time = time.time() |
| print('函数index的执行时间为:', end_time - start_time) |
| |
| |
| get_time() |
但是这样函数体代码不够灵活,只能对index函数使用,如何对其他函数使用?
3.使用参数可以传参变换统计的函数
| 利用参数,将统计的函数名传给参数,变换传参即变化统计的函数 |
| |
| def get_time(v): |
| start_time = time.time() |
| v() |
| end_time = time.time() |
| print('函数index的执行时间为:', end_time - start_time) |
| |
| |
| get_time(home) |
| get_time(index) |
| ---------------res-------------------- |
| from home |
| 函数index的执行时间为: 1.003127098083496 |
| from index |
| 函数index的执行时间为: 3.0050511360168457 |
如何通过原调用函数的方式,去使用新功能?
4.使用闭包函数来传参
| 通过闭包函数来将传参的方法变简便 |
| 上述直接分装形参传值不写,不写参数,可以用闭包函数 |
| def outer(v): |
| def get_time(): |
| start_time = time.time() |
| v() |
| end_time = time.time() |
| print('函数index的执行时间为:', end_time - start_time) |
| return get_time |
| |
| res = outer(index) |
| res() |
| res = outer(home) |
| res() |
| ---------------res-------------------- |
| from index |
| 函数index的执行时间为: 3.005207061767578 |
| from home |
| 函数index的执行时间为: 1.004741907119751 |
如何使用原函数名来调用具有新功能的函数?
5.使用赋值语句使调用原函数名时具有新功能
| 将原函数名 绑定向 扩展了新功能的函数体代码 |
| def outer(v): |
| def get_time(): |
| start_time = time.time() |
| v() |
| end_time = time.time() |
| print('函数index的执行时间为:', end_time - start_time) |
| return get_time |
| |
| index = outer(index) |
| index() |
| home = outer(home) |
| home() |
| ---------------res-------------------- |
| from index |
| 函数index的执行时间为: 3.005207061767578 |
| from home |
| 函数index的执行时间为: 1.004741907119751 |
如果想调用有参函数怎么办?
6.为闭包函数引入参数,兼容有参函数
| 考虑想要装饰的函数有参数、以及返回值 |
| |
| |
| import time |
| |
| def index(a): |
| time.sleep(3) |
| print('from index',a) |
| |
| def func1(a, b): |
| time.sleep(0.2) |
| print('from func1', a) |
| |
| |
| def outer(res): |
| def get_time(*args, **kwargs): |
| start_time = time.time() |
| res(*args, **kwargs) |
| end_time = time.time() |
| print('函数index的执行时间为:', end_time - start_time) |
| |
| return get_time |
| |
| |
| index = outer(index) |
| index(1) |
| func1 = outer(func1) |
| func1(1, 2) |
| ---------------res-------------------- |
| from index 1 |
| 函数index的执行时间为: 3.005033016204834 |
| from func1 1 |
| 函数index的执行时间为: 0.20508694648742676 |
如果函数有返回值,则需要为装饰器添加返回值
7.为闭包函数引入返回值,兼容返回值
| 添加返回值import time |
| |
| import time |
| |
| def index(a): |
| time.sleep(3) |
| print('from index',a) |
| return 'index' |
| |
| def func1(a, b): |
| time.sleep(0.2) |
| print('from func1', a) |
| return 'func1' |
| |
| def outer(func): |
| def get_time(*args, **kwargs): |
| start_time = time.time() |
| res = func(*args, **kwargs) |
| end_time = time.time() |
| print('函数index的执行时间为:', end_time - start_time) |
| |
| return res |
| return get_time |
| |
| |
| index = outer(index) |
| res1 = index(1) |
| print(res1) |
| |
| func1 = outer(func1) |
| res2 = func1(1,2) |
| print(res2) |
| ---------------res-------------------- |
| from index 1 |
| 函数index的执行时间为: 3.005223035812378 |
| index |
| from func1 1 |
| 函数index的执行时间为: 0.20263981819152832 |
| func1 |
3.装饰器模版
| def outer(func): |
| def inner(*args, **kwargs): |
| |
| res = func(*args, **kwargs) |
| |
| return res |
| return inner |
4.装饰器语法糖
| 用符号@装饰器的函数名,调用该装饰器,且把它正下方的函数名当作是参传入,然后将返回的结果重新赋值给原函数名 |
| def outer(func): |
| def inner(*args, **kwargs): |
| |
| res = func(*args, **kwargs) |
| |
| return res |
| return inner |
| """ |
| 语法糖的原理: |
| |
| 语法糖 @装饰器的函数名,调用该装饰器,且把它正下方的函数名当作是参数传入@后面函数名,并调用,然后将返回的结果重新赋值给原函数名 |
| """ |
| @outer |
| def func(): |
| print('from func') |
| return 'func' |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY