对Python闭包与延迟绑定的方法详解


Posted in Python onJanuary 07, 2019

Python闭包可能会在面试或者是工作中经常碰到,而提到Python的延迟绑定,肯定就离不开闭包的理解,今天总结下 关于闭包的概念以及一个延迟绑定的面试题。

Python闭包

1、什么是闭包,闭包必须满足以下3个条件:

必须是一个嵌套的函数。

闭包必须返回嵌套函数。

嵌套函数必须引用一个外部的非全局的局部自由变量。

举个栗子

# 嵌套函数但不是闭包
def nested():
  def nst():
    print('i am nested func %s' % nested.__name__)
  nst()

# 闭包函数
def closure():
  var = 'hello world' # 非全局局部变量

  def cloe():
    print(var) # 引用var

  return cloe # 返回内部函数


cl = closure()
cl()

2、闭包优点

避免使用全局变量

可以提供部分数据的隐藏

可以提供更优雅的面向对象实现

优点1,2 就不说了,很容易理解,关于第三个,例如当在一个类中实现的方法很少时,或者仅有一个方法时,就可以选择使用闭包。

举个栗子

# 用类实现一个加法的类是这样
class _Add(object):
  def __init__(self, a, b):
    self.a = a
    self.b = b

  def add(self):
    return self.a + self.b

# 用闭包实现
def _Add(a):
  def add(b):
    return a + b

  return add 

ad = _Add(1) # 是不是很像类的实例化
print(ad(1)) # out:2
print(ad(2)) # out:3
print(ad(3)) # out:4

闭包的概念差不多就是这样了。

Python 延迟绑定

结合一个题目来说明:

def multipliers():
  return [lambda x : i*x for i in range(4)]

print [m(2) for m in multipliers()] 

output:
# [6, 6, 6, 6]

其实这个题目,可能目的是想输出:[0, 2, 4, 6],如何改进才能输出这个结果呢?

def multipliers():
  # 添加了一个默认参数i=i
  return [lambda x, i=i: i*x for i in range(4)]

print [m(2) for m in multipliers()] 

output:
# [0, 2, 4, 6]

multipliers就是一个闭包函数了

def multipliers():
  return [lambda x : i*x for i in range(4)]
 # multipliers内嵌套一个匿名函数
 # 该匿名函数引用外部非全局变量 i
 # 返回该嵌套函数
print [m(2) for m in multipliers()]

下面来解释为什么输出结果是[6,6,6,6]。

运行代码,代码从第6行开始运行,解释器碰到了一个列表解析,循环取multipliers()函数中的值,而multipliers()函数返回的是一个列表对象,这个列表中有4个元素,每个元素都是一个匿名函数(实际上说是4个匿名函数也不完全准确,其实是4个匿名函数计算后的值,因为后面for i 的循环不光循环了4次,同时提还提供了i的变量引用,等待4次循环结束后,i指向一个值i=3,这个时候,匿名函数才开始引用i=3,计算结果。所以就会出现[6,6,6,6],因为匿名函数中的i并不是立即引用后面循环中的i值的,而是在运行嵌套函数的时候,才会查找i的值,这个特性也就是延迟绑定)

# 为了便于理解,你可以想象下multipliers内部是这样的(这个是伪代码,并不是准确的):

def multipliers():
  return [lambda x: 3 * x, lambda x: 3 * x, lambda x: 3 * x, lambda x: 3 * x]

因为Python解释器,遇到lambda(类似于def),只是定义了一个匿名函数对象,并保存在内存中,只有等到调用这个匿名函数的时候,才会运行内部的表达式,而for i in range(4) 是另外一个表达式,需等待这个表达式运行结束后,才会开始运行lambda 函数,此时的i 指向3,x指向2

那我们来看下,添加了一个i=i,到底发生了什么?

def multipliers():
  # 添加了一个默认参数i=i
  return [lambda x, i=i: i*x for i in range(4)]

添加了一个i=i后,就给匿名函数,添加了一个默认参数,而python函数中的默认参数,是在python 解释器遇到def(i=i)或lambda 关键字时,就必须初始化默认参数,此时for i in range(4),每循环一次,匿名函数的默认参数i,就需要找一次i的引用,i=0时,第一个匿名函数的默认参数值就是0,i=1时,第二个匿名函数的默认参数值就是1,以此类推。

# 为了便于理解,你可以想象下multipliers内部是这样的(这个是伪代码只是为了理解):

def multipliers():
  return [lambda x,i=0: i*x, lambda x,i=1: i*x, lambda x,i=2: i*x, lambda x,i=3:i*x i=3]
# x的引用是2 所以output的结果就是:[0,2,4,6]

当然你的i=i,也可以改成a=i。

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

Python的延迟绑定其实就是只有当运行嵌套函数的时候,才会引用外部变量i,不运行的时候,并不是会去找i的值,这个就是第一个函数,为什么输出的结果是[6,6,6,6]的原因。

以上就是自己对于Python闭包和延迟绑定的理解。

这篇对Python闭包与延迟绑定的方法详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python标准库之随机数 (math包、random包)介绍
Nov 25 Python
Python实现的文本编辑器功能示例
Jun 30 Python
基于python3 OpenCV3实现静态图片人脸识别
May 25 Python
对python csv模块配置分隔符和引用符详解
Dec 12 Python
解决nohup执行python程序log文件写入不及时的问题
Jan 14 Python
flask/django 动态查询表结构相同表名不同数据的Model实现方法
Aug 29 Python
python基于opencv检测程序运行效率
Dec 28 Python
Python如何在DataFrame增加数值
Feb 14 Python
python实现简单的购物程序代码实例
Mar 03 Python
5款实用的python 工具推荐
Oct 13 Python
python3列表删除大量重复元素remove()方法的问题详解
Jan 04 Python
Django实现简单的分页功能
Feb 22 Python
python将控制台输出保存至文件的方法
Jan 07 #Python
对Python捕获控制台输出流的方法详解
Jan 07 #Python
python print输出延时,让其立刻输出的方法
Jan 07 #Python
Python3 单行多行万能正则匹配方法
Jan 07 #Python
Python字符串的全排列算法实例详解
Jan 07 #Python
Python 3.3实现计算两个日期间隔秒数/天数的方法示例
Jan 07 #Python
Python进阶之自定义对象实现切片功能
Jan 07 #Python
You might like
The specified CGI application misbehaved by not returning a complete set of HTTP headers
2011/03/31 PHP
PHP面向对象程序设计OOP继承用法入门示例
2016/12/27 PHP
Yii框架布局文件的动态切换操作示例
2019/11/11 PHP
通用javascript脚本函数库 方便开发
2009/10/13 Javascript
解决iframe的frameborder在chrome/ff/ie下的差异
2010/08/12 Javascript
cookie在javascript中的使用技巧以及隐私在服务器端的设置
2012/12/03 Javascript
jquery validation验证身份证号,护照,电话号码,email(实例代码)
2013/11/06 Javascript
javascript模拟地球旋转效果代码实例
2013/12/02 Javascript
JS修改iframe页面背景颜色的方法
2015/04/01 Javascript
JavaScript中用toString()方法返回时间为字符串
2015/06/12 Javascript
jQuery+HTML5实现图片上传前预览效果
2015/08/20 Javascript
超漂亮的Bootstrap 富文本编辑器summernote
2016/04/05 Javascript
jQuery选择器总结之常用元素查找方法
2016/08/04 Javascript
JQuery和PHP结合实现动态进度条上传显示
2016/11/23 Javascript
Javascript基础回顾之(二) js作用域
2017/01/31 Javascript
微信小程序使用Socket的实例
2017/09/19 Javascript
EasyUI框架 使用Ajax提交注册信息的实现代码
2017/09/27 Javascript
Vue手把手教你撸一个 beforeEnter 钩子函数
2018/04/24 Javascript
详解Vue+axios+Node+express实现文件上传(用户头像上传)
2018/08/10 Javascript
策略模式实现 Vue 动态表单验证的方法
2019/09/16 Javascript
转换layUI的数据表格中的日期格式方法
2019/09/19 Javascript
小程序简单两栏瀑布流效果的实现
2019/12/18 Javascript
JavaScript中while循环的基础使用教程
2020/08/11 Javascript
python中的__init__ 、__new__、__call__小结
2014/04/25 Python
常见python正则用法的简单实例
2016/06/21 Python
python实现字典(dict)和字符串(string)的相互转换方法
2017/03/01 Python
selenium+python自动化测试之使用webdriver操作浏览器的方法
2019/01/23 Python
浅谈Python的条件判断语句if/else语句
2019/03/21 Python
python语言基本语句用法总结
2019/06/11 Python
python 爬取马蜂窝景点翻页文字评论的实现
2020/01/20 Python
keras的load_model实现加载含有参数的自定义模型
2020/06/22 Python
SheIn沙特阿拉伯:女装在线
2020/03/23 全球购物
工程造价管理专业大专生求职信
2013/10/06 职场文书
努力学习演讲稿
2014/05/10 职场文书
保护水资源的标语
2014/06/17 职场文书
庆元旦活动总结
2014/07/09 职场文书