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备份Mysql脚本
Aug 11 Python
Python兔子毒药问题实例分析
Mar 05 Python
python Matplotlib画图之调整字体大小的示例
Nov 20 Python
python logging重复记录日志问题的解决方法
Jul 12 Python
Python使用Selenium模块实现模拟浏览器抓取淘宝商品美食信息功能示例
Jul 18 Python
Python使用while循环花式打印乘法表
Jan 28 Python
详解如何设置Python环境变量?
May 13 Python
Python内存管理实例分析
Jul 10 Python
python3.6+selenium实现操作Frame中的页面元素
Jul 16 Python
Django 拆分model和view的实现方法
Aug 16 Python
python3.8动态人脸识别的实现示例
Sep 21 Python
PyCharm 2020.1版安装破解注册码永久激活(激活到2089年)
Sep 24 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&amp;mysql(五)
2006/10/09 PHP
表单复选框向PHP传输数据的代码
2007/11/13 PHP
php中apc缓存使用示例
2013/12/25 PHP
PHP中each与list用法分析
2016/01/08 PHP
javascript中怎么做对象的类型判断
2013/11/11 Javascript
Node.js与PHP、Python的字符处理性能对比
2014/07/06 Javascript
一个获取第n个元素节点的js函数
2014/09/02 Javascript
js实现鼠标滚轮控制图片缩放效果的方法
2015/02/20 Javascript
js获取滚动距离的方法
2015/05/30 Javascript
JavaScript中的Math.sin()方法使用详解
2015/06/15 Javascript
JavaScript实现Base64编码转换
2016/04/23 Javascript
jQuery删除当前节点元素
2016/12/07 Javascript
jQuery中的一些小技巧
2017/01/18 Javascript
bootstrap组件之导航组件使用方法
2017/01/19 Javascript
vuejs绑定class和style样式
2017/04/11 Javascript
详解Angular之constructor和ngOnInit差异及适用场景
2017/06/22 Javascript
JavaScript之DOM_动力节点Java学院整理
2017/07/03 Javascript
微信小程序出现wx.navigateTo页面不跳转问题的解决方法
2017/12/26 Javascript
react build 后打包发布总结
2018/08/24 Javascript
Vue请求JSON Server服务器数据的实现方法
2018/11/02 Javascript
python实现socket客户端和服务端简单示例
2014/02/24 Python
python 基础教程之Map使用方法
2017/01/17 Python
python实现守护进程、守护线程、守护非守护并行
2018/05/05 Python
django项目中使用手机号登录的实例代码
2019/08/15 Python
pyenv与virtualenv安装实现python多版本多项目管理
2019/08/17 Python
python读取ini配置文件过程示范
2019/12/23 Python
Flask模板引擎Jinja2使用实例
2020/04/23 Python
英国领先的家庭时尚品牌:Peacocks
2018/01/11 全球购物
阿迪达斯新加坡官方网站:adidas新加坡
2019/12/06 全球购物
亚马逊加拿大网站:Amazon.ca
2020/01/06 全球购物
医院办公室主任职责
2013/12/29 职场文书
护理专业学生职业生涯规划范文
2014/03/11 职场文书
民政局标准版离婚协议书
2014/12/01 职场文书
2014年煤矿安全工作总结
2014/12/04 职场文书
复兴之路观后感3000字
2015/06/02 职场文书
golang操作redis的客户端包有多个比如redigo、go-redis
2022/04/14 Golang