Python 基础教程之闭包的使用方法


Posted in Python onSeptember 29, 2017

Python 基础教程之闭包的使用方法

前言:

闭包(closure)是函数式编程的重要的语法结构。函数式编程是一种编程范式 (而面向过程编程和面向对象编程也都是编程范式)。在面向过程编程中,我们见到过函数(function);在面向对象编程中,我们见过对象(object)。函数和对象的根本目的是以某种逻辑方式组织代码,并提高代码的可重复使用性(reusability)。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。

不同的语言实现闭包的方式不同。Python以函数对象为基础,为闭包这一语法结构提供支持的 (我们在特殊方法与多范式中,已经多次看到Python使用对象来实现一些特殊的语法)。Python一切皆对象,函数这一语法结构也是一个对象。在函数对象中,我们像使用一个普通对象一样使用函数对象,比如更改函数对象的名字,或者将函数对象作为参数进行传递。

 函数对象的作用域

和其他对象一样,函数对象也有其存活的范围,也就是函数对象的作用域。函数对象是使用def语句定义的,函数对象的作用域与def所在的层级相同。比如下面代码,我们在line_conf函数的隶属范围内定义的函数line,就只能在line_conf的隶属范围内调用。

def line_conf():
  def line(x):
    return 2*x+1
  print(line(5))  # within the scope


line_conf()
print(line(5))    # out of the scope

line函数定义了一条直线(y = 2x + 1)。可以看到,在line_conf()中可以调用line函数,而在作用域之外调用line将会有下面的错误:

NameError: name 'line' is not defined

说明这时已经在作用域之外。

同样,如果使用lambda定义函数,那么函数对象的作用域与lambda所在的层级相同。

 闭包

函数是一个对象,所以可以作为某个函数的返回结果。

def line_conf():
  def line(x):
    return 2*x+1
  return line    # return a function object

my_line = line_conf()
print(my_line(5))

上面的代码可以成功运行。line_conf的返回结果被赋给line对象。上面的代码将打印11。

如果line()的定义中引用了外部的变量,会发生什么呢?

def line_conf():
  b = 15
  def line(x):
    return 2*x+b
  return line    # return a function object

b = 5
my_line = line_conf()
print(my_line(5))

我们可以看到,line定义的隶属程序块中引用了高层级的变量b,但b信息存在于line的定义之外 (b的定义并不在line的隶属程序块中)。我们称b为line的环境变量。事实上,line作为line_conf的返回值时,line中已经包括b的取值(尽管b并不隶属于line)。

上面的代码将打印25,也就是说,line所参照的b值是函数对象定义时可供参考的b值,而不是使用时的b值。

一个函数和它的环境变量合在一起,就构成了一个闭包(closure)。在Python中,所谓的闭包是一个包含有环境变量取值的函数对象。环境变量取值被保存在函数对象的__closure__属性中。比如下面的代码:

def line_conf():
  b = 15
  def line(x):
    return 2*x+b
  return line    # return a function object

b = 5
my_line = line_conf()
print(my_line.__closure__)
print(my_line.__closure__[0].cell_contents)

__closure__里包含了一个元组(tuple)。这个元组中的每个元素是cell类型的对象。我们看到第一个cell包含的就是整数15,也就是我们创建闭包时的环境变量b的取值。

下面看一个闭包的实际例子:

def line_conf(a, b):
  def line(x):
    return ax + b
  return line

line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5), line2(5))

这个例子中,函数line与环境变量a,b构成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个环境变量的取值,这样,我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。

如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。利用闭包,我们实际上创建了泛函。line函数定义一种广泛意义的函数。这个函数的一些方面已经确定(必须是直线),但另一些方面(比如a和b参数待定)。随后,我们根据line_conf传递来的参数,通过闭包的形式,将最终函数确定下来。

闭包与并行运算

闭包有效的减少了函数所需定义的参数数目。这对于并行运算来说有重要的意义。在并行运算的环境下,我们可以让每台电脑负责一个函数,然后将一台电脑的输出和下一台电脑的输入串联起来。最终,我们像流水线一样工作,从串联的电脑集群一端输入数据,从另一端输出数据。这样的情境最适合只有一个参数输入的函数。闭包就可以实现这一目的。

并行运算正称为一个热点。这也是函数式编程又热起来的一个重要原因。函数式编程早在1950年代就已经存在,但应用并不广泛。然而,我们上面描述的流水线式的工作并行集群过程,正适合函数式编程。由于函数式编程这一天然优势,越来越多的语言也开始加入对函数式编程范式的支持。

如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

Python 相关文章推荐
Python读写Redis数据库操作示例
Mar 18 Python
python获取当前日期和时间的方法
Apr 30 Python
通过Python实现自动填写调查问卷
Sep 06 Python
Python wxpython模块响应鼠标拖动事件操作示例
Aug 23 Python
python中如何使用分步式进程计算详解
Mar 22 Python
pyqt5实现按钮添加背景图片以及背景图片的切换方法
Jun 13 Python
使用python爬取抖音视频列表信息
Jul 15 Python
解决python中显示图片的plt.imshow plt.show()内存泄漏问题
Apr 24 Python
如何在python中判断变量的类型
Jul 29 Python
python按照list中字典的某key去重的示例代码
Oct 13 Python
详解python爬取弹幕与数据分析
Nov 14 Python
详解Python类和对象内容
Jun 22 Python
python下实现二叉堆以及堆排序的示例
Sep 29 #Python
Python数据结构与算法之链表定义与用法实例详解【单链表、循环链表】
Sep 28 #Python
Python实现压缩和解压缩ZIP文件的方法分析
Sep 28 #Python
Python有序字典简单实现方法示例
Sep 28 #Python
python操作MySQL 模拟简单银行转账操作
Sep 27 #Python
python利用urllib和urllib2访问http的GET/POST详解
Sep 27 #Python
python django使用haystack:全文检索的框架(实例讲解)
Sep 27 #Python
You might like
php上传文件并存储到mysql数据库的方法
2015/03/16 PHP
php实现指定字符串中查找子字符串的方法
2015/03/17 PHP
php+webSoket实现聊天室示例代码(附源码)
2017/02/17 PHP
javascript操作cookie的文章(设置,删除cookies)
2010/04/01 Javascript
子窗口、父窗口和Silverlight之间的相互调用
2010/08/16 Javascript
读jQuery之十四 (触发事件核心方法)
2011/08/23 Javascript
JavaScript等比例缩放图片控制超出范围的图片
2013/08/06 Javascript
JQueryiframe页面操作父页面中的元素与方法(实例讲解)
2013/11/19 Javascript
解决jquery中美元符号命名冲突问题
2014/01/08 Javascript
jQuery的end()方法使用详解
2015/07/15 Javascript
基于jQuery实现鼠标点击导航菜单水波动画效果附源码下载
2016/01/06 Javascript
JS弹出窗口插件zDialog简单用法示例
2016/06/12 Javascript
浅谈jquery上下滑动的注意事项
2016/10/13 Javascript
bootstrap table 表格中增加下拉菜单末行出现滚动条的快速解决方法
2017/01/05 Javascript
vue中实现上传文件给后台实例详解
2019/08/22 Javascript
JavaScript实现五子棋小游戏
2020/10/26 Javascript
Vue实现手机号、验证码登录(60s禁用倒计时)
2020/12/19 Vue.js
[58:54]EG vs RNG 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/18 DOTA
零基础写python爬虫之抓取糗事百科代码分享
2014/11/06 Python
Python判断文本中消息重复次数的方法
2016/04/27 Python
利用Python实现网络测试的脚本分享
2017/05/26 Python
Django 中使用流响应处理视频的方法
2018/07/20 Python
Python之time模块的时间戳,时间字符串格式化与转换方法(13位时间戳)
2019/08/12 Python
基于HTML5超酷摄像头(HTML5 webcam)拍照功能实现代码
2012/12/13 HTML / CSS
希尔顿酒店中国网站:Hilton中国
2017/03/11 全球购物
法国票务网站:Ticketmaster法国
2018/07/09 全球购物
中海讯通笔试题
2015/09/15 面试题
趣味比赛活动方案
2014/02/15 职场文书
新闻发布会活动策划方案
2014/09/15 职场文书
法人单位适用的授权委托书
2014/09/19 职场文书
合同和协议有什么区别?
2014/10/08 职场文书
中学生清明节演讲稿
2015/03/18 职场文书
遗愿清单观后感
2015/06/09 职场文书
如何撰写出一份完美的商业计划书?
2019/07/12 职场文书
压缩Redis里的字符串大对象操作
2021/06/23 Redis
MySQL分区以及建索引的方法总结
2022/04/13 MySQL