Python延迟绑定问题原理及解决方案


Posted in Python onAugust 04, 2020

延迟绑定出现在闭包问题中。下面我们看一个闭包的例子:

def (n):
  def mul(x):
    return n*x
  return mul
double = gen_mul(2)
doubled_value = double(6)

可以看出满足闭包的几点:

  • 有内部函数
  • 内部函数引用了外部函数中的自由变量
  • 内部函数被返回

闭包的优点:

  • 可以避免使用全局变量
  • 可以持久化变量,达到静态变量的作用

闭包的缺点:

  • 可能会消耗大量的内存
  • 可能会导致内存泄漏

当然缺点可以通过人为避免。

现在我们来看看另一个会引出延迟绑定的例子:

def multipliers():
  return [lambda x : i * x for i in range(4)]
print([m(2) for m in multipliers()]) # [6,6,6,6]

上边的例子会输出[6,6,6,6],而不是我们预期的[0,2,4,6]。

这就是延迟绑定导致的结果。具体过程我们可以来分析下:
执行第三行时,会先执行multipliers函数,然后执行函数中的列表解析式。在每一次迭代的时候都会生成一个匿名函数(这里只是定义)作为元素。然后回到第三行,遍历返回的列表中的匿名函数,传入参数2并执行。此时函数类似于这样:

def noname(x):
return i * x

我们知道Python查找变量的作用域链的顺序依次为LEGB:

局部变量(L)->外部函数中的局部变量(E)->全局变量(G)->内置变量(B)

非常重要的一点我们需要知道:Python的作用域在编译时就已经形成了,而不是在运行时,函数的作用域与其被调用的位置无关。

那么在本例中,上面的noname函数体中的i从何而来呢?当然首先会到multipliers函数的局部变量中去寻找。此时i的值已经为3,所以出现这种让人”费解”的现象。

那么现在我们既然已经知道了原因,那么要怎样解决呢?

我们可以将迭代的i值直接注入到匿名函数的函数体中,这里给出两种方法:

通过为参数设置默认值,这是因为在编译时就会计算确定默认值:

def multipliers_ch1():
return [lambda m,x=i : m * x for i in range(4)]

通过内置函数partial:

from functools import partial
def multipliers_ch2():
  return [partial(lambda m,x : m * x,i) for i in range(4)]

利用生成器的延迟计算:

def multipliers_ch3():
  for m in range(4):
    yield lambda x: m * x

partial及生成器的内容会在以后分享。

运行结果

print([m(2) for m in multipliers_ch1()]) # [0,2,4,6]
print([m(2) for m in multipliers_ch2()]) # [0,2,4,6]
print([m(2) for m in multipliers_ch3()]) # [0,2,4,6]

注:

自由变量:指未在本地作用域中绑定的变量,我们可通过访问函数的code属性进行查看:

fun.code.co_freevars

LEGB: 可看该部分解释

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python之父谈Python的未来形式
Jul 01 Python
使用python实现BLAST
Feb 12 Python
Python实现按照指定要求逆序输出一个数字的方法
Apr 19 Python
django将图片上传数据库后在前端显式的方法
May 25 Python
把csv文件转化为数组及数组的切片方法
Jul 04 Python
python 实现倒排索引的方法
Dec 25 Python
Python多版本开发环境管理工具介绍
Jul 03 Python
在matplotlib中改变figure的布局和大小实例
Apr 23 Python
numpy 矩阵形状调整:拉伸、变成一位数组的实例
Jun 18 Python
浅谈pytorch中的BN层的注意事项
Jun 23 Python
Python文件夹批处理操作代码实例
Jul 21 Python
python利用xpath爬取网上数据并存储到django模型中
Feb 26 Python
Python 使用生成器代替线程的方法
Aug 04 #Python
详解Tensorflow不同版本要求与CUDA及CUDNN版本对应关系
Aug 04 #Python
python读取xml文件方法解析
Aug 04 #Python
如何利用python进行时间序列分析
Aug 04 #Python
通过实例简单了解Python sys.argv[]使用方法
Aug 04 #Python
哪种Python框架适合你?简单介绍几种主流Python框架
Aug 04 #Python
python logging 重复写日志问题解决办法详解
Aug 04 #Python
You might like
php简单开启gzip压缩方法(zlib.output_compression)
2013/04/13 PHP
php5.3不能连接mssql数据库的解决方法
2014/12/27 PHP
smarty简单应用实例
2015/11/03 PHP
图文详解phpstorm配置Xdebug进行调试PHP教程
2016/06/13 PHP
Laravel事件监听器用法实例分析
2019/03/12 PHP
PHP中->和=>的含义及使用示例解析
2020/08/06 PHP
Aster vs Newbee BO5 第一场2.19
2021/03/10 DOTA
js页面跳转常用的几种方式
2010/11/25 Javascript
JS获取select-option-text_value的方法
2013/12/26 Javascript
基于NodeJS的前后端分离的思考与实践(四)安全问题解决方案
2014/09/26 NodeJs
JavaScript sup方法入门实例(把字符串显示为上标)
2014/10/20 Javascript
基于jquery css3实现点击动画弹出表单源码特效
2015/08/31 Javascript
javascript字符串函数汇总
2015/12/06 Javascript
浅析JavaScript回调函数应用
2016/05/22 Javascript
Vue 2.x教程之基础API
2017/03/06 Javascript
JavaScript数据结构中栈的应用之表达式求值问题详解
2017/04/11 Javascript
使用D3.js+Vue实现一个简单的柱形图
2018/08/05 Javascript
从0到1构建vueSSR项目之node以及vue-cli3的配置
2019/03/07 Javascript
Vue动态创建注册component的实例代码
2019/06/14 Javascript
vue组件库的在线主题编辑器的实现思路
2020/04/03 Javascript
[52:37]完美世界DOTA2联赛循环赛 Forest vs DM BO2第一场 10.29
2020/10/29 DOTA
python中利用await关键字如何等待Future对象完成详解
2017/09/07 Python
Python中enumerate函数代码解析
2017/10/31 Python
Flask解决跨域的问题示例代码
2018/02/12 Python
python生成lmdb格式的文件实例
2018/11/08 Python
Python爬取商家联系电话以及各种数据的方法
2018/11/10 Python
python实现微信定时每天和女友发送消息
2019/04/29 Python
基于Python实现下载网易音乐代码实例
2020/08/10 Python
会计自我鉴定
2013/11/02 职场文书
高二学生评语大全
2014/04/25 职场文书
市场督导岗位职责
2015/04/10 职场文书
外出考察学习心得体会
2016/01/18 职场文书
2016年保险公众宣传日活动总结
2016/04/05 职场文书
人为什么会“幸灾乐祸”?
2019/08/06 职场文书
粗暴解决CUDA out of memory的问题
2021/05/22 Python
react中useState使用:如何实现在当前表格直接更改数据
2022/08/05 Javascript