Python中用函数作为返回值和实现闭包的教程


Posted in Python onApril 27, 2015

函数作为返回值

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。

我们来实现一个可变参数的求和。通常情况下,求和的函数是这样定义的:

def calc_sum(*args):
  ax = 0
  for n in args:
    ax = ax + n
  return ax

但是,如果不需要立刻求和,而是在后面的代码中,根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数!

def lazy_sum(*args):
  def sum():
    ax = 0
    for n in args:
      ax = ax + n
    return ax
  return sum

当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数:

>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function sum at 0x10452f668>

调用函数f时,才真正计算求和的结果:

>>> f()
25

在这个例子中,我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。

请再注意一点,当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数:

>>> f1 = lazy_sum(1, 3, 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1==f2
False

f1()和f2()的调用结果互不影响。
闭包

注意到返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可不容易。

另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行。我们来看一个例子:

def count():
  fs = []
  for i in range(1, 4):
    def f():
       return i*i
    fs.append(f)
  return fs

f1, f2, f3 = count()

在上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都返回了。

你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果是:

>>> f1()
9
>>> f2()
9
>>> f3()
9

全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。

返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

>>> def count():
...   fs = []
...   for i in range(1, 4):
...     def f(j):
...       def g():
...         return j*j
...       return g
...     fs.append(f(i))
...   return fs
... 
>>> f1, f2, f3 = count()
>>> f1()
1
>>> f2()
4
>>> f3()
9

缺点是代码较长,可利用lambda函数缩短代码。

Python 相关文章推荐
python进阶教程之函数对象(函数也是对象)
Aug 30 Python
研究Python的ORM框架中的SQLAlchemy库的映射关系
Apr 25 Python
Python实现大文件排序的方法
Jul 10 Python
python+matplotlib绘制旋转椭圆实例代码
Jan 12 Python
Python数据类型中的“冒号“[::]——分片与步长操作示例
Jan 24 Python
儿童编程python入门
May 08 Python
python pandas读取csv后,获取列标签的方法
Nov 12 Python
Pandas中resample方法详解
Jul 02 Python
python语言线程标准库threading.local解读总结
Nov 10 Python
对django 2.x版本中models.ForeignKey()外键说明介绍
Mar 30 Python
Django框架配置mysql数据库实现过程
Apr 22 Python
python 监控服务器是否有人远程登录(详细思路+代码)
Dec 18 Python
Python中利用sorted()函数排序的简单教程
Apr 27 #Python
Python中的filter()函数的用法
Apr 27 #Python
Python中的map()函数和reduce()函数的用法
Apr 27 #Python
PyMongo安装使用笔记
Apr 27 #Python
Windows下PyMongo下载及安装教程
Apr 27 #Python
Python操作MongoDB数据库PyMongo库使用方法
Apr 27 #Python
Python的函数的一些高阶特性
Apr 27 #Python
You might like
Zend Framework实现多服务器共享SESSION数据的方法
2016/03/22 PHP
Zend Framework教程之Zend_Helpers动作助手ViewRenderer用法详解
2016/07/20 PHP
PHP获取页面执行时间的方法(推荐)
2016/12/10 PHP
Laravel如何使用数据库事务及捕获事务失败后的异常详解
2017/10/23 PHP
jquery 文本上下无缝滚动,鼠标放上去就停止 小例子
2013/06/05 Javascript
javaScript函数中执行C#代码中的函数方法总结
2013/08/07 Javascript
jquery $.each() 使用小探
2013/08/23 Javascript
使用phantomjs进行网页抓取的实现代码
2014/09/29 Javascript
node.js中的fs.readSync方法使用说明
2014/12/17 Javascript
浏览器复制插件zeroclipboard使用指南
2016/03/26 Javascript
基于JavaScript实现购物网站商品放大镜效果
2016/09/06 Javascript
js实现简单的手风琴效果
2017/02/27 Javascript
基于iScroll实现下拉刷新和上滑加载效果
2017/07/18 Javascript
js实现网页的两个input标签内的数值加减(示例代码)
2017/08/15 Javascript
Vue2 模板template的四种写法总结
2018/02/23 Javascript
JavaScript执行环境及作用域链实例分析
2018/08/01 Javascript
vue之debounce属性被移除及处理详解
2019/11/13 Javascript
JavaScript实现页面高亮操作提示和蒙板
2021/01/04 Javascript
Python中利用sorted()函数排序的简单教程
2015/04/27 Python
python创建关联数组(字典)的方法
2015/05/04 Python
快速查询Python文档方法分享
2017/12/27 Python
基于python 微信小程序之获取已存在模板消息列表
2019/08/05 Python
python实现音乐播放器 python实现花框音乐盒子
2020/02/25 Python
python实现超级玛丽游戏
2020/03/18 Python
什么是python的id函数
2020/06/11 Python
Python datetime模块使用方法小结
2020/06/18 Python
Python中的With语句的使用及原理
2020/07/29 Python
Django缓存Cache使用详解
2020/11/30 Python
使用Filters滤镜弥补CSS3的跨浏览器问题以及兼容低版本IE
2013/01/23 HTML / CSS
使用HTML5中的contentEditable来将多行文本自动增高
2016/03/01 HTML / CSS
阿里巴巴英国:Alibaba英国
2019/12/11 全球购物
土木工程专业自荐信
2013/10/04 职场文书
保险专业求职信
2014/07/07 职场文书
班主任先进事迹材料
2014/12/17 职场文书
幼儿园母亲节活动总结
2015/02/10 职场文书
考教师资格证不要错过的4个最佳时机
2019/07/17 职场文书