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实现定时同步本机与北京时间的方法
Mar 24 Python
用Python的Django框架来制作一个RSS阅读器
Jul 22 Python
详解Python的Django框架中的Cookie相关处理
Jul 22 Python
Python数据结构与算法之图结构(Graph)实例分析
Sep 05 Python
python实现求解列表中元素的排列和组合问题
Mar 15 Python
Django 使用Ajax进行前后台交互的示例讲解
May 28 Python
django框架基于模板 生成 excel(xls) 文件操作示例
Jun 19 Python
关于PyTorch源码解读之torchvision.models
Aug 17 Python
Python @property装饰器原理解析
Jan 22 Python
Python字符串split及rsplit方法原理详解
Jun 29 Python
python 爬虫如何实现百度翻译
Nov 16 Python
Restful_framework视图组件代码实例解析
Nov 17 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扩展
2010/03/12 PHP
php连接数据库代码应用分析
2011/05/29 PHP
不支持fsockopen但支持culr环境下下ucenter与modoer通讯问题
2011/08/12 PHP
linux环境apache多端口配置虚拟主机的方法深入介绍
2013/06/09 PHP
php自定义hash函数实例
2015/05/05 PHP
php PDO异常处理详解
2016/11/20 PHP
PHP count()函数讲解
2019/02/03 PHP
ASP.NET中使用后端代码注册脚本 生成JQUERY-EASYUI的界面错位的解决方法
2010/06/12 Javascript
jQuery简单图表peity.js使用示例
2014/05/02 Javascript
node.js中的fs.fchmodSync方法使用说明
2014/12/16 Javascript
Javascript实现通过选择周数显示开始日和结束日的实现代码
2016/05/30 Javascript
Angular4编程之表单响应功能示例
2017/12/13 Javascript
使用Electron构建React+Webpack桌面应用的方法
2017/12/15 Javascript
解决vue 更改计算属性后select选中值不更改的问题
2018/03/02 Javascript
node puppeteer(headless chrome)实现网站登录
2018/05/09 Javascript
原生JS实现前端本地文件上传
2018/09/08 Javascript
深入理解react 组件类型及使用场景
2019/03/07 Javascript
如何检查一个对象是否为空
2019/04/11 Javascript
利用python实现命令行有道词典的方法示例
2017/01/31 Python
Python使用numpy实现BP神经网络
2018/03/10 Python
对numpy中轴与维度的理解
2018/04/18 Python
python批量从es取数据的方法(文档数超过10000)
2018/12/27 Python
Python中logging实例讲解
2019/01/17 Python
Django REST framework内置路由用法
2019/07/26 Python
VSCode中自动为Python文件添加头部注释
2019/11/14 Python
Python写捕鱼达人的游戏实现
2020/03/31 Python
python模块如何查看
2020/06/16 Python
毕业生造价工程师求职信
2013/10/17 职场文书
小学生安全责任书
2014/07/25 职场文书
机关党总支领导班子整改方案
2014/09/20 职场文书
2014年向国旗敬礼活动总结
2014/09/27 职场文书
小学少先队活动总结
2015/05/08 职场文书
《小蝌蚪找妈妈》教学反思
2016/02/23 职场文书
2016五四青年节活动总结范文
2016/04/06 职场文书
python制作图形界面的2048游戏, 基于tkinter
2021/04/06 Python
Python Django ORM连表正反操作技巧
2021/06/13 Python