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入门之三角函数全解【收藏】
Nov 08 Python
Python实现对文件进行单词划分并去重排序操作示例
Jul 10 Python
python opencv实现切变换 不裁减图片
Jul 26 Python
Python装饰器限制函数运行时间超时则退出执行
Apr 09 Python
python实现支付宝转账接口
May 07 Python
搞清楚 Python traceback的具体使用方法
May 13 Python
python实现批量文件重命名
Oct 31 Python
python飞机大战pygame碰撞检测实现方法分析
Dec 17 Python
Python类中self参数用法详解
Feb 13 Python
python3 配置logging日志类的操作
Apr 08 Python
QML用PathView实现轮播图
Jun 03 Python
Python基于内置函数type创建新类型
Oct 22 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 If Else(elsefi) 语句
2013/04/07 PHP
如何设置mysql允许外网访问
2013/06/04 PHP
页面乱码问题的根源及其分析
2013/08/09 PHP
php+MySQL判断update语句是否执行成功的方法
2014/08/28 PHP
JQUERY CHECKBOX全选,取消全选,反选方法三
2008/08/30 Javascript
JS对象与JSON格式数据相互转换
2012/02/20 Javascript
基于jquery和svg实现超炫酷的动画特效
2014/12/09 Javascript
JavaScript中将数组进行合并的基本方法讲解
2016/03/07 Javascript
jQuery+ajax+asp.net获取Json值的方法
2016/06/08 Javascript
Ajax跨域实现代码(后台jsp)
2017/01/21 Javascript
在vue-cli脚手架中配置一个vue-router前端路由
2017/07/03 Javascript
基于vue-cli 打包时抽离项目相关配置文件详解
2018/03/07 Javascript
微信小程序项目实践之主页tab选项实现
2018/07/18 Javascript
vue组件(全局,局部,动态加载组件)
2018/09/02 Javascript
js实现计时器秒表功能
2019/12/16 Javascript
Vue2.x和Vue3.x的双向绑定原理详解
2020/11/05 Javascript
[02:41]DOTA2英雄基础教程 谜团
2013/12/10 DOTA
python创建只读属性对象的方法(ReadOnlyObject)
2013/02/10 Python
Python下的Mysql模块MySQLdb安装详解
2014/04/09 Python
Python制作数据导入导出工具
2015/07/31 Python
python多线程socket编程之多客户端接入
2017/09/12 Python
python探索之BaseHTTPServer-实现Web服务器介绍
2017/10/28 Python
解决安装pycharm后不能执行python脚本的问题
2019/01/19 Python
win10下安装Anaconda的教程(python环境+jupyter_notebook)
2019/10/23 Python
Pycharm修改python路径过程图解
2020/05/22 Python
python3环境搭建过程(利用Anaconda+pycharm)完整版
2020/08/19 Python
详解python3类型注释annotations实用案例
2021/01/20 Python
python如何发送带有附件、正文为HTML的邮件
2021/02/27 Python
CSS3实现酷炫的3D旋转透视效果
2019/11/21 HTML / CSS
STUBHUB日本:购买和出售全球活动门票
2018/07/01 全球购物
如何将字串String转换成整数int
2015/02/21 面试题
财务管理个人自荐书范文
2013/11/24 职场文书
培训讲师岗位职责
2014/04/13 职场文书
计算机网络专业自荐信
2014/07/04 职场文书
四风问题对照检查材料整改措施
2014/09/27 职场文书
phpQuery解析HTML乱码问题(补充官网未列出的乱码解决方案)
2021/04/01 PHP