Python中的返回函数与闭包
返回函数,顾名思义,就是高阶函数可以把函数作为return值返回。与闭包的关系是:闭包需要以返回函数的形式实现。
一. 返回函数
比如我们有一个求和函数:
>>> def calc_sum(num_list):
s = 0
for i in num_list:
s += i
return s >>> calc_sum([1,2,3,4])
10
当我们不需要立刻求和,而是后面根据需要再计算结果时,我们可以返回求和的函数,而不是直接返回计算结果。这就是返回函数。
>>> def lazy_calc_sum(num_list):
def calc_sum():
s = 0
for i in num_list:
s += i
return s
return calc_sum >>> f_lazy = lazy_calc_sum([1,2,3,4])
>>> f_lazy
<function lazy_calc_sum.<locals>.calc_sum at 0x0000003A8D92E9D8>
>>> f_lazy()
10
很显然,这样能让我们根据需求,节省计算资源。
二. 闭包
在上面的例子中,我们在函数lazy_clac_sum
中又定义了函数calc_sum
,并且,内部函数calc_sum
可以引用外部函数lazy_calc_sum
的参数和局部变量,当lazy_calc_sum
返回函数calc_sum
时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”。
如果让定义更加清晰一些: 如果在一个内部函数里对在外部作用域(但不是在全局作用域)的变量进行引用,但不在全局作用域里,则这个内部函数就是一个闭包。
实际上,闭包的用处/优点有两条:
- 从函数外可以读取函数内部的变量
- 让这些变量的值始终保持在内存中(也可以理解为保留当前运行环境)
下面例子是,我们创建了一个下载download函数,然后下载次数一直存储在内存中。
>>> def download_enter(download_times):
def download():
nonlocal download_times
download_times += 1
print("This is the %s time download" % download_times)
return download >>>
>>> d = download_enter(0)
>>> d()
This is the 1 time download
>>> d()
This is the 2 time download
>>> d()
This is the 3 time download
下面的例子是闭包根据外部作用域的局部变量来得到不同的结果,这有点像一种类似配置功能的作用,我们可以修改外部的变量,闭包根据这个变量展现出不同的功能。比如有时我们需要对某些文件的特殊行进行分析,先要提取出这些特殊行。
def make_filter(keep):
def the_filter(file_name):
file = open(file_name)
lines = file.readlines()
file.close()
filter_doc = [i for i in lines if keep in i]
return filter_doc
return the_filter
如果我们需要取得文件"result.txt"中含有"pass"关键字的行,则可以这样使用例子程序
filter = make_filter("pass")
filter_result = filter("result.txt")
以上两种使用场景,用面向对象也是可以很简单的实现的,但是在用Python进行函数式编程时,闭包对数据的持久化以及按配置产生不同的功能,是很有帮助的。
关于闭包的2个常见错误:
1. 尝试在闭包中改变外部作用域的局部变量
def foo():
a = 1
def bar():
a = a + 1
return a
return bar
>>> c = foo()
>>> print c()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in bar
UnboundLocalError: local variable 'a' referenced before assignment
这段程序的本意是要通过在每次调用闭包函数时都对变量a进行递增的操作。但在实际使用时,a = a + 1的a会被python解释器认为是bar()函数的局部变量,从而引起“referenced before assignment”的错误。
解决方法有两个:
方法一:将a设置为一个容器,比如列表List (不推荐)
方法二:将a声明为nonlocal变量(仅在Python3支持),这样声明过后,就不会被认为是bar()函数的局部变量,而是会到上一层函数环境中寻找这个变量。
下面是例子:
>>> def foo():
a = 1
b = [1]
def bar():
nonlocal a
a = a + 1
b[0] = b[0] + 1
return a,b[0]
return bar >>> c = foo()
>>> print(c())
(2, 2)
>>> print(c())
(3, 3)
>>> print(c())
(4, 4)
2. 误以为返回的函数就已执行,对执行结果误判
直接举例子说明:
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():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
return fs
结果是:
>>> f1, f2, f3 = count()
>>> f1()
1
>>> f2()
4
>>> f3()
9
Python中的返回函数与闭包的更多相关文章
- C 返回函数与闭包的考虑
#include <stdio.h> typedef int (*fun)(); fun closure(int i) { int squ() { return i*i; } return ...
- Python:Base4(map,reduce,filter,自定义排序函数(sorted),返回函数,闭包,匿名函数(lambda) )
1.python把函数作为参数: 在2.1小节中,我们讲了高阶函数的概念,并编写了一个简单的高阶函数: def add(x, y, f): return f(x) + f(y) 如果传入abs作为参数 ...
- python中“生成器”、“迭代器”、“闭包”、“装饰器”的深入理解
python中"生成器"."迭代器"."闭包"."装饰器"的深入理解 一.生成器 1.生成器定义:在python中,一边 ...
- python --- Python中的callable 函数
python --- Python中的callable 函数 转自: http://archive.cnblogs.com/a/1798319/ Python中的callable 函数 callabl ...
- python中使用zip函数出现<zip object at 0x02A9E418>
在Python中使用zip函数,出现<zip object at 0x02A9E418>错误的原因是,你是用的是python2点多的版本,python3.0对python做了改动 zip方 ...
- [转载]python中multiprocessing.pool函数介绍
原文地址:http://blog.sina.com.cn/s/blog_5fa432b40101kwpi.html 作者:龙峰 摘自:http://hi.baidu.com/xjtukanif/blo ...
- Python 中的isinstance函数
解释: Python 中的isinstance函数,isinstance是Python中的一个内建函数 语法: isinstance(object, classinfo) 如果参数object是cla ...
- Python中的map()函数和reduce()函数的用法
Python中的map()函数和reduce()函数的用法 这篇文章主要介绍了Python中的map()函数和reduce()函数的用法,代码基于Python2.x版本,需要的朋友可以参考下 Py ...
- python中multiprocessing.pool函数介绍_正在拉磨_新浪博客
python中multiprocessing.pool函数介绍_正在拉磨_新浪博客 python中multiprocessing.pool函数介绍 (2010-06-10 03:46:5 ...
随机推荐
- Python测量时间,用time.time还是time.clock
在计算机领域有多种时间.第一种称作CPU时间或执行时间,用于测量在执行一个程序时CPU所花费的时间.第二种称作挂钟时间,测量执行一个程序时的总时间.挂钟时间也被称作流逝时间或运行时间.与CPU时间相比 ...
- 2.Spring Cloud初相识--------Eureka服务注册与消费
前言: 1.Eureka介绍: Spring Cloud Eureka,使用Netflix Eureka来实现服务注册与发现,它既包含了服务端组件,也包含了客户端组件,并且服务端与客户端均采用Java ...
- singnal 13 was raised
在app运行过程中按下home键或者其他原因app被挂起,socket连接不会断开,服务器为了节省资源,在一段时间后会主动关闭这个连接.当玩家再次切回到游戏后,前端并不知道这个连接已经断开了,继续通过 ...
- ES6初识-Proxy和Reflect
{ let obj={ time:'2017-03-11', name:'net', _r:123 }; let monitor=new Proxy(obj,{ // 拦截对象属性的读取 get( ...
- DB设计工具——dbschema
Preface I've got a db design job about meeting room booking system last week.There're many s ...
- TP5 行为Behavior用法说明
TP5 行为Behavior用法说明 无论是tp3还是在tp5中,行为都是一个非常重要的概念,关于太多的理论知识,就不多说了,不了解的请查看开发文档:TP5 行为概述 以下,就由代码来一步一步实现行为 ...
- MYSQL SQL高级查询技巧
1.UNION,EXCEPT,INTERSECT运算符 A,UNION 运算符 UNION 运算符通过组合其他两个结果表(例如 TABLE1 和 TABLE2)并消去表中任何重复行而派生出一个结果表. ...
- jira安装说明
阅读目录 1.1 jira说明 1.2 安装配置jira 1.3 web界面访问 1.4 创建第一个项目 1.5 参考文献 回到顶部 1.1 jira说明 JIRA是Atlassian公司出品的项目与 ...
- 【异常】The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone.
异常错误:The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone ...
- PHP.29-TP框架商城应用实例-后台6-商品会员添加-价格、级别
首先把需求分析搞清楚 主要实现两个功能 1.会员管理,设置成为会员的要求 2.添加商品时,可设置会员优惠价格 具体实现 1.建表[会员级别限定表p39_member_level{Id,级别名称,积分下 ...