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搭建简易服务器分析与实现
Dec 15 Python
用Python中的wxPython实现最基本的浏览器功能
Apr 14 Python
python获取命令行输入参数列表的实例代码
Jun 23 Python
Python 微信爬虫完整实例【单线程与多线程】
Jul 06 Python
如何通过python的fabric包完成代码上传部署
Jul 29 Python
python利用7z批量解压rar的实现
Aug 07 Python
Pandas-Cookbook 时间戳处理方式
Dec 07 Python
Python实现非正太分布的异常值检测方式
Dec 09 Python
python保留小数位的三种实现方法
Jan 07 Python
使用tensorflow显示pb模型的所有网络结点方式
Jan 23 Python
pytorch 计算ConvTranspose1d输出特征大小方式
Jun 23 Python
记一次django内存异常排查及解决方法
Aug 07 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中文转拼音的实现代码
2014/02/11 PHP
用JavaScrpt实现文件夹简单轻松加密的实现方法图文
2008/09/08 Javascript
javascript new一个对象的实质
2010/01/07 Javascript
js获取判断上传文件后缀名的示例代码
2014/02/19 Javascript
关于JavaScript命名空间的一些心得
2014/06/07 Javascript
使用jquery清空、复位整个输入域
2015/04/02 Javascript
JavaScript获取网页中第一个链接ID的方法
2015/04/03 Javascript
轻量级的原生js日历插件calendar.js使用指南
2015/04/28 Javascript
手机开发必备技巧:javascript及CSS功能代码分享
2015/05/25 Javascript
javascript中传统事件与现代事件
2015/06/23 Javascript
Node.js巧妙实现Web应用代码热更新
2015/10/22 Javascript
JS组件Bootstrap Table表格多行拖拽效果实现代码
2015/12/08 Javascript
jQuery mobile 移动web(6)
2015/12/20 Javascript
基于JS实现数字+字母+中文的混合排序方法
2016/06/06 Javascript
Angular之指令Directive用法详解
2017/03/01 Javascript
vue.js的提示组件
2017/03/02 Javascript
jacascript DOM节点——元素节点、属性节点、文本节点
2017/04/18 Javascript
javascript 玩转Date对象(实例讲解)
2017/07/11 Javascript
node前端模板引擎Jade之标签的基本写法
2018/05/11 Javascript
Angular 中使用 FineReport不显示报表直接打印预览
2019/08/21 Javascript
jquery validate 实现动态增加/删除验证规则操作示例
2019/10/28 jQuery
python基础教程之udp端口扫描
2014/02/10 Python
利用Tkinter(python3.6)实现一个简单计算器
2017/12/21 Python
Python数据结构之栈、队列及二叉树定义与用法浅析
2018/12/27 Python
Django后台admin的使用详解
2019/07/08 Python
Python collections中的双向队列deque简单介绍详解
2019/11/04 Python
HTML5应用之文件上传
2016/12/30 HTML / CSS
雅萌 (YA-MAN) :日本美容家电领域的龙头企业
2017/05/12 全球购物
Sunglasses Shop德国站:欧洲排名第一的太阳镜网站
2017/08/01 全球购物
关于雷锋的演讲稿
2014/05/10 职场文书
预防传染病方案
2014/06/14 职场文书
寒暑假实习证明书模板
2014/11/29 职场文书
大学生学期个人总结
2015/02/12 职场文书
乡镇科协工作总结2015
2015/05/19 职场文书
居住证明范文
2015/06/17 职场文书
Python 多线程处理任务实例
2021/11/07 Python