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 除法小技巧
Sep 06 Python
Python实现字符串与数组相互转换功能示例
Sep 22 Python
50行Python代码实现人脸检测功能
Jan 23 Python
Python zip()函数用法实例分析
Mar 17 Python
python3 读写文件换行符的方法
Apr 09 Python
Python 3.x 判断 dict 是否包含某键值的实例讲解
Jul 06 Python
Python求一批字符串的最长公共前缀算法示例
Mar 02 Python
python fuzzywuzzy模块模糊字符串匹配详细用法
Aug 29 Python
python爬虫开发之selenium模块详细使用方法与实例全解
Mar 09 Python
详解Python高阶函数
Aug 15 Python
Python Selenium库的基本使用教程
Jan 04 Python
Python将QQ聊天记录生成词云的示例代码
Feb 10 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
php获取mysql字段名称和其它信息的例子
2014/04/14 PHP
php解析json数据实例
2014/08/19 PHP
PHP下载远程文件到本地存储的方法
2015/03/24 PHP
php读取XML的常见方法实例总结
2017/04/25 PHP
根据地区不同显示时间的javascript代码
2007/08/13 Javascript
删除节点的jquery代码
2014/01/13 Javascript
jQuery动画特效实例教程
2014/08/29 Javascript
JS表格组件神器bootstrap table详解(基础版)
2015/12/08 Javascript
微信和qq时间格式模板实例详解
2016/10/21 Javascript
利用JQuery直接调用asp.net后台的简单方法
2016/10/27 Javascript
webpack常用配置项配置文件介绍
2016/11/07 Javascript
JS新包管理工具yarn和npm的对比与使用入门
2016/12/09 Javascript
bootstrap配合Masonry插件实现瀑布式布局
2017/01/18 Javascript
AngularJs 利用百度地图API 定位当前位置 获取地址信息
2017/01/18 Javascript
详解webpack+angular2开发环境搭建
2017/06/28 Javascript
Bootstrap Table实现定时刷新数据的方法
2018/08/13 Javascript
微信小程序提取公用函数到util.js及使用方法示例
2019/01/10 Javascript
Vue发布项目实例讲解
2019/07/17 Javascript
我所理解的JavaScript中的this指向
2020/09/04 Javascript
[49:43]VG vs FNATIC 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/17 DOTA
python 计算文件的md5值实例
2017/01/13 Python
详解python中的json和字典dict
2018/06/22 Python
django认证系统 Authentication使用详解
2019/07/22 Python
python 使用socket传输图片视频等文件的实现方式
2019/08/07 Python
python中用logging实现日志滚动和过期日志删除功能
2019/08/20 Python
Python3 使用selenium插件爬取苏宁商家联系电话
2019/12/23 Python
python实现简单遗传算法
2020/09/18 Python
美国领先的医疗警报服务:Philips Lifeline
2018/03/12 全球购物
信息专业学生学习的自我评价
2014/02/17 职场文书
中学教师教育感言
2014/02/21 职场文书
创文明城市标语
2014/06/16 职场文书
党政领导班子四风问题对照检查材料思想汇报
2014/10/02 职场文书
2015年师德师风承诺书
2015/01/22 职场文书
水电施工员岗位职责
2015/04/11 职场文书
Go 自定义package包设置与导入操作
2021/05/06 Golang
详解PHP用mb_string处理windows中文字符
2021/05/26 PHP