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 相关文章推荐
合并Excel工作薄中成绩表的VBA代码,非常适合教育一线的朋友
Apr 09 Python
Pycharm学习教程(5) Python快捷键相关设置
May 03 Python
Python迭代器定义与简单用法分析
Apr 30 Python
python中使用iterrows()对dataframe进行遍历的实例
Jun 09 Python
django DRF图片路径问题的解决方法
Sep 10 Python
Python基于Tkinter模块实现的弹球小游戏
Dec 27 Python
对python多线程中Lock()与RLock()锁详解
Jan 11 Python
带你认识Django
Jan 15 Python
Tensorflow: 从checkpoint文件中读取tensor方式
Feb 10 Python
Python实现网络聊天室的示例代码(支持多人聊天与私聊)
Jan 27 Python
Python 的演示平台支持 WSGI 接口的应用
Apr 20 Python
python数字图像处理之对比度与亮度调整示例
Jun 28 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实现登录注册之BootStrap表单功能
2017/09/03 PHP
PHP依赖注入原理与用法分析
2018/08/21 PHP
laravel-admin select框默认选中的方法
2019/10/03 PHP
Yii框架 session 数据库存储操作方法示例
2019/11/18 PHP
仿校内登陆框,精美,给那些很厉害但是没有设计天才的程序员
2008/11/24 Javascript
for 循环性能比较 提高for循环的效率
2009/03/19 Javascript
jquery select(列表)的操作(取值/赋值)
2009/08/06 Javascript
JavaScript动态改变div属性的实现方法
2015/07/22 Javascript
jquery实现简易的移动端验证表单
2015/11/08 Javascript
原生js配合cookie制作保存路径的拖拽
2015/12/29 Javascript
轻松实现JavaScript图片切换
2016/01/12 Javascript
Node.js 条形码识别程序构建思路详解
2016/02/14 Javascript
vue实现表格数据的增删改查
2017/07/10 Javascript
swiper动态改变滑动内容的实现方法
2018/01/17 Javascript
详解vue-meta如何让你更优雅的管理头部标签
2018/01/18 Javascript
Vue 用Vant实现时间选择器的示例代码
2019/10/25 Javascript
vue实现评论列表功能
2019/10/25 Javascript
vue进入页面时不在顶部,检测滚动返回顶部按钮问题及解决方法
2019/10/30 Javascript
解决vuex数据页面刷新后初始化操作
2020/07/26 Javascript
[04:47]DOTA2-潍坊风行电子俱乐部探秘
2014/08/08 DOTA
python中print的不换行即时输出的快速解决方法
2016/07/20 Python
Python实现将MySQL数据库表中的数据导出生成csv格式文件的方法
2018/01/11 Python
Python 串口读写的实现方法
2019/06/12 Python
解决python 虚拟环境删除包无法加载的问题
2020/07/13 Python
瑞士国际航空官网:SWISS
2016/07/21 全球购物
英国健身超市:Fitness Superstore
2019/06/17 全球购物
The Hut英国:英国领先的豪华在线百货商店
2019/07/26 全球购物
波兰最大的宠物用品网上商店:FERA.PL
2019/08/11 全球购物
vue+django实现下载文件的示例
2021/03/24 Vue.js
配件采购员岗位职责
2013/12/03 职场文书
主持人婚宴答谢词
2014/01/28 职场文书
机关作风建设整改方案
2014/10/27 职场文书
2014年校务公开工作总结
2014/12/18 职场文书
银行员工考核评语
2014/12/31 职场文书
2016年员工年度考核评语
2015/12/02 职场文书
mysql性能优化以及配置连接参数设置
2022/05/06 MySQL