闭包函数,装饰器详解
闭包函数
【1】.闭包函数两大特征
1.闭:定义在函数内部的函数
2.包:内部函数使用了外层函数名称空间中的名字
【2】在代码中怎么走
def outer():
def inner():
print(f'真简单')
return inner
res = outer()
在上述代码中怎么走
-
第一步定义outer函数,什么时候执行这个函数里的代码,要遇到outer()才会执行,
-
第二步到res = outer(),=是赋值符号所以先看outer()
-
第三步有了outer()才会执行里面的代码,进入到里面执行def inner():
-
第四步因为没有inner()所以inner函数里面的代码不会执行,只会返回一个inner函数名
-
第五步,然后把inner这个函数名给了res这个变量,使得res指向了inner函数里的函数体
- 然后res()就相当于inner()
def outer():
def inner():
print(f'真简单')
return inner
res = outer()
res() # 真简单
现在inner()是不是闭包函数
def outer():
def inner():
print(f'真简单')
return inner
# 现在只是只是在outer函数内部,并没有使用了外层函数名称空间中的名字
def outer():
x = 741
def inner():
print(f'真简单',x)
return inner
# 现在inner算是真正的闭包函数
执行代码顺序
# 最后输出999还是741
def outer():
x = 741
def inner():
print(f'真简单',x)
return inner
x=999
res = outer()
res() # 真简单 741
# inner是在outer 局部里面的的局部里面的,inner本身没有x,所以他会往上找,就找到outer局部里面的x=741,跟x=999无关
【3】闭包函数实际应用
闭包函数是给函数整体传参的另一种方式
函数体传参方式一:形参
def index(username):
print(username)
# 函数体代码需要什么就可以在形参中写什么
index('silence') # silence # 在调用index这个函数时就必须给他一个username参数
函数体传参方式二:闭包
def outer():
username = 'silence'
def index():
print(username) # 永远使用的都是silence
return index
res = outer()
res() # silence
多个传递
def outer(username):
# username = 'silence'
def index():
print(username)
return index
res = outer('silence')
#形参username与值silence临时绑定>>>放在了outer局部名称空间中
res() # silence
rest = outer('happy')
rest() # happy
代码执行顺序图
装饰器
【1】简介
装饰器是由名称空间,函数名,闭包函数等整合到一起的产物
【2】装饰器定义
在不改变被装饰对象原有的调用方式和内部代码的情况下
给被装饰对象添加新功能
【3】装饰器原则
对扩展开发,对修改封闭
【4】装饰器了解
import time
print(time.time()) #1713259590.5478706
# 上述数字是时间戳:1970年1月1日0时0分0秒距离刚刚代码运行的秒数
import time
time.sleep(4) # 让程序原地等待四秒
print("你真棒")
import time
def index():
time.sleep(3)
print('from index')
star_time = time.time() # 函数执行之前获得一个时间戳
index()
end_time = time.time() # 函数执行之后获得一个时间戳
print(end_time - star_time) # 两个时间戳的差值就是函数的执行时间
# from index 3.000314950942993
【5】代码怎么生成一个装饰器
封装成函数之后,调用方式改变了,不符合装饰器原则
#缺陷一:
# 代码写死了,无法统计其他函数的执行时间 只有index()
# 如何解决:----> 将函数名通过形参的形式传入,例如get_time(in)
# 缺陷二:
# 封装成函数后 以下代码中 index()调用方式改变成了get_time(index)
# 通过形参的形式传入解决不掉
import time
def index():
time.sleep(3)
print('from index')
def get_time():
star_time = time.time() # 函数执行之前获得一个时间戳
index()
end_time = time.time() # 函数执行之后获得一个时间戳
print(end_time - star_time) # 两个时间戳的差值就是函数的执行时间
get_time()
装饰器简易版本(无参装饰器)
import time
def index():
time.sleep(3)
print('from index')
def outer(func):
def get_time():
star_time = time.time() # 函数执行之前获得一个时间戳
func() # 调用了真正的index函数
end_time = time.time() # 函数执行之后获得一个时间戳
print(end_time - star_time) # 两个时间戳的差值就是函数的执行时间
return get_time
index = outer(index)
index() # 看似调用的是index其实调用的是get_time
print(index) # 全局名称空间中的index指向的是get_time函数体代码
执行顺序
装饰器进阶版本(有参装饰器)
解决的是参数问题
程序报错
import time
def home(a):
time.sleep(3)
print('from home')
def outer(func_name):
def get_time():
star_time = time.time()
func_name()
end_time = time.time()
print(end_time - star_time)
return get_time
home = outer(home)
home() # 报错 home() missing 1 required positional argument: 'a'
函数体代码里需要一个参数,有两种办法
- 方式一:直接形参传
- 方式二:再进行一次闭包操作
import time
def home(a):
time.sleep(3)
print('from home')
def outer(func_name):
def get_time(a): # 意思就是在调用get_time时候也要给他传递一个参数
star_time = time.time()
func_name(a)
end_time = time.time()
print(end_time - star_time)
return get_time
home = outer(home)
home('silence') # home就是调用的get_time 所以需要一个参数'silence'
# from home
# 3.011603593826294
我们根本不知道即将执行的函数是否需要参数但是还想让他正常运行,于是
import time
def index():
time.sleep(2)
print("from index")
def home(a):
time.sleep(3)
print('from home')
def outer(func_name):
def get_time(*arga,**kwargs):
star_time = time.time()
func_name(*arga,**kwargs)
end_time = time.time()
print(end_time - star_time)
return get_time
home = outer(home)
home('silence')
index = outer(index)
index()
# from home
# 3.008333921432495
# from index
# 2.011590003967285
图解
完整版解释器
解决的是返回值问题
import time
def index(a,b):
time.sleep(2)
print(a, b, "from index")
return '加油!'
def outer(func_name):
def get_time(*arga,**kwargs):
star_time = time.time()
func_name(*arga,**kwargs)
end_time = time.time()
print(end_time - star_time)
return get_time
index = outer(index) # 接收函数的返回值
res = index(11,22)
print(res)
# 11 22 from index
# 2.0102128982543945
# None
为什么是返回None,其实是拿到的是get_time的返回值,但是get_time没有返回值,所以是None
想让index有返回值怎么办
import time
def index(a,b):
time.sleep(2)
print(a, b, "from index")
return '加油!'
def outer(func_name):
def get_time(*arga,**kwargs):
star_time = time.time()
res=func_name(*arga,**kwargs) # 执行真正的index函数
end_time = time.time()
print(end_time - star_time)
return res
return get_time
index = outer(index) # 接收函数的返回值
res = index(11,22)
print(res)
装饰器模板
# 万能模板
def outer(func_name): # func_name 用于接收被装饰的对象(函数)
def inner(*args, **kwargs):
print('执行被装饰函数之前,可以做的额外操作')
res = func_name(*args, **kwargs) # 执行真正的被装饰函数
print('执行被装饰函数后,可以做的额外操作')
return res # 返回真正函数的返回值
return inner
例题:验证登陆
def home(*args, **kwargs):
print('我是home函数,只有silence才能调用')
def outer(func_name):
def inner(*args, **kwargs):
# 执行home前先获取用户数据
username = input('username>>>:').strip()
password = input('password>>>:').strip()
if username == 'silence' and password == '741':
res = func_name(*args, **kwargs) # 执行真正的被装饰函数
return res # 返回真正函数的返回值
else:
print('你没有资格执行')
return inner
home = outer(home)
home()
# username>>>:silence
# password>>>:741
# 我是home函数,只有silence才能调用
或者是
def index():
print("Hello")
def outer(func_name):
def inner(*args, **kwargs):
# 执行home前先获取用户数据
username = input('username>>>:').strip()
password = input('password>>>:').strip()
if username == 'silence' and password == '741':
res = func_name(*args, **kwargs) # 执行真正的被装饰函数
return res # 返回真正函数的返回值
else:
print('你没有资格执行')
return inner
# home = outer(home)
# home()
index = outer(index)
index()
# username>>>:silence
# password>>>:741
# Hello
装饰器语法糖
仅仅是让代码编写的更加好看,简洁
def outer(func_name):
def inner(*args, **kwargs):
print('执行函数之前的操作')
res = func_name(*args, **kwargs)
return res
return inner
@outer # 写在想要装饰的函数的头上 等价于index = outer(index)
def index(*args, **kwargs):
print('from index')
@outer # 等价于home = outer(home)
def home():
print('from home')
# index = outer(index) # 总感觉这一行代码有些low
index()
home()
# 执行函数之前的操作
# from index
# 执行函数之前的操作
# from home
语法糖内部原理
1.使用时最好跟在被装饰对象的上方
2.语法糖会自动将下面紧挨着的函数名传给@后面的函数调用
装饰器修复技术
1. help可以查看指定函数的注释信息
ef outer(func_name):
def inner(*args, **kwargs):
print('执行被装饰器之前可以做的操作')
res = func_name(*args, **kwargs)
return res
return inner
@outer # 写在想要装饰的函数的头上 等价于index = outer(index)
def index(*args, **kwargs):
print('from index')
# 等价于home = outer(home)
def home():
'''这是一个注释'''
print('from home')
print(help(home))
help(len)
# home()
# 这是一个注释
# len(obj, /)
# Return the number of items in a container.
2. 装饰器修复
语法固定搭配
- from functools import wraps
- @wraps(func_name)
from functools import wraps
def outer(func_name): # func_name 用于接收被装饰的对象(函数)
@wraps(func_name)
def inner(*args, **kwargs):
print('执行被装饰器之前可以做的操作')
res = func_name(*args, **kwargs)
return res
return inner
@outer # 写在想要装饰的函数的头上 等价于index = outer(index)
def index(*args, **kwargs):
print('from index')
# 等价于home = outer(home)
@outer
def home():
print('from home')
index()
home()
【推荐】国内首个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