实例讲解Python的函数闭包使用中应注意的问题
作者:TypingQuietly 发布时间:2022-05-17 23:29:53
昨天正当我用十成一阳指功力戳键盘、昏天暗地coding的时候,正好被人问了一个问题,差点没收好功,洪荒之力侧漏震伤桌边的人,废话不多说,先上栗子(精简版,只为说明问题):
from functools import wraps
from time import sleep
def retry(attempts=3, wait=2):
if attempts < 0 or attempts > 5:
retry_times = 3
else:
retry_times = attempts
if wait < 0 or wait > 5:
retry_wait = 2
else:
retry_wait = after
def retry_decorator(func):
@wraps(func)
def wrapped_function(*args, **kwargs):
while retry_times > 0:
try:
return func(*args, **kwargs)
except :
sleep(retry_wait)
retry_times -= 1
return wrapped_function
return retry_decorator
简易版的retry装饰器,需要的变量被闭包完美捕捉,逻辑也挺简单明了。问的人说逻辑看着挺正常的,但就是一直报变量retry_times找不到(unresolved reference)的错误提示。
没错仔细捋一下,这是一道送分题呢:闭包捕获的变量(retry_times,retry_wait)相当时引用的retry函数的局部变量,当在wrapped_function的局部作用于里面操作不可变类型的数据时,会生成新的局部变量,但是新生成的局部变量retry_times在使用时还没来得及初始化,因此会提示找不到变量;retry_wait相反能被好好的使用到。
python是duck-typing的编程语言,就算有warning照样跑,写个简单到极限的的函数,用一下装饰器,在wrapped_function逻辑里打个断点看一下各个变量的值也是很快能找到问题的(直接跑也能看到错误:UnboundLocalError: local variable 'retry_attempts' referenced before assignment, 至少比warning msg有用):
@retry(7, 8)
def test():
print 23333
raise Exception('Call me exception 2333.')
if __name__ == '__main__':
test()
output: UnboundLocalError: local variable 'retry_times' referenced before assignment
要解决这种问题也好办,用一个可变的容器把要用的不可变类型的数据包装一下就行了(说个好久没写C#代码记不太清楚完全不负责任的题外话,就像在C#.net里面,碰到闭包的时候,会自动生成一个混淆过名字的类然后把要被捕捉的值当作类的属性存着,这样在使用的时候就能轻松get,著名的老赵好像有一篇文章讲Lazy Evaluation的好像涉及到这个话题):
def retry(attempts=3, wait=2):
temp_dict = {
'retry_times': 3 if attempts < 0 or attempts > 5 else attempts,
'retry_wait': 2 if wait < 0 or wait > 5 else wait
}
def retry_decorate(fn):
@wraps(fn)
def wrapped_function(*args, **kwargs):
print id(temp_dict), temp_dict
while temp_dict.get('retry_times') > 0:
try:
return fn(*args, **kwargs)
except :
sleep(temp_dict.get('retry_wait'))
temp_dict['retry_times'] = temp_dict.get('retry_times') - 1
print id(temp_dict), temp_dict
print id(temp_dict), temp_dict
return wrapped_function
return retry_decorate
@retry(7, 8)
def test():
print 23333
raise Exception('Call me exception 2333.')
if __name__ == '__main__':
test()
输出:
4405472064 {'retry_wait': 2, 'retry_times': 3}
4405472064 {'retry_wait': 2, 'retry_times': 3}
23333
4405472064 {'retry_wait': 2, 'retry_times': 2}
23333
4405472064 {'retry_wait': 2, 'retry_times': 1}
23333
4405472064 {'retry_wait': 2, 'retry_times': 0}
从output中可以看到,用dict包装后,程序能够正常的工作,和预期的一致,其实我们也可以从函数的闭包的值再次确认:
>>> test.func_closure[1].cell_contents
{'retry_wait': 2, 'retry_times': 2}
我是结尾,PEACE!


猜你喜欢
- 1. 得到安全字符串,在查询中使用,过滤单引号。Function Get_SafeStr(str) &nb
- 本文实例为大家分享了python绘制温度变化雷达图的具体代码,供大家参考,具体内容如下假设某天某地每三个小时取样的气温为针对温度变化趋势绘制
- 由于不同的项目需要用不同的python版本,于是使用Anaconda来进行版本管理,现记录一下经验:在官网下载并安装好Anaconda以后(
- 获取航线信息并且制作成图航线信息航线信息查询网站本次实例使用的航班号为 CES5496查询后在network中可以寻找到如下内容https:
- Portable Document Format(可移植文档格式),或者PDF是一种文件格式,可以用于跨操作系统的呈现和文档交换。尽管PDF
- 最近需要将使用keras训练的模型移植到手机上使用, 因此需要转换到tensorflow的二进制模型。折腾一下午,终于找到一个合适的方法,废
- 在进制学习时候,细心的小伙伴不免都发现unicher函数的存在,没错能够经常看到的,也就是关于进制的转化,那肯定有小伙伴要开心起来了,因为进
- 跑代码时,在命令行给python程序传入bool参数,但无法传入False,无论传入True还是False,程序里面都是True。下面是代码
- DQN算法是DeepMind团队提出的一种深度强化学习算法,在许多电动游戏中达到人类玩家甚至超越人类玩家的水准,本文就带领大家了解一下这个算
- 接上章《pygame实现俄罗斯方块游戏(基础篇1)》继续写俄罗斯方块游戏五、计算方块之间的碰撞在Panel类里增加函数def check_o
- 您可以将SQL Server 数据库引擎升级到 SQL Server 2008。SQL Server 安装程序只需最少的用户干预就可升级 S
- Pytorch四维Tensor转图片并保存最近在复现一篇 * 码的过程中,想要输出中间图片的结果图,通过debug发现在pytorch网络中
- 许多应用程序中都会有日志模块,用于记录系统在运行过程中的一些关键信息,以便于对系统的运行状况进行跟踪。在.NET平台中,有非常著名的第三方开
- Django默认Path转换器str:匹配任何非空字符串,但不含斜杠/,如果你没有专门指定转换器,那么这个是默认使用的;int:匹配0和正整
- 一、条件控制Python条件语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块,而计算机很多自动化任务,也是根据
- 经常会遇到下载的文件或电子书,名字中间都包含了一些网址信息,实际使用中由于名字太长不方便,下面的脚本使用正则表达式来对目录下的所有文件重命名
- 1. js的数据类型1.1 js引入方式<!DOCTYPE html><html lang="en"&
- 最近在使用爬虫爬取数据时,经常会返回403代码,大致意思是该IP访问过于频繁,被限制访问。限制IP访问网站最常用的反爬手段了,其实破解也很容
- 数据库连接池概念:其实就是一个容器(集合),存放数据库连接的容器。概念:其实就是一个容器(集合),存放数据库连接的容器。 &n
- 接口性能测试时,接口请求参数是根据一定的规则拼接后进行MD5加密后再进行传参,因此借助于python脚本实现,则可以有效提升测试效率。1.分