返回函数,顾名思义,就是高阶函数可以把函数作为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()结果应该是149,但实际结果是:

>>> 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中的返回函数与闭包的更多相关文章

  1. C 返回函数与闭包的考虑

    #include <stdio.h> typedef int (*fun)(); fun closure(int i) { int squ() { return i*i; } return ...

  2. Python:Base4(map,reduce,filter,自定义排序函数(sorted),返回函数,闭包,匿名函数(lambda) )

    1.python把函数作为参数: 在2.1小节中,我们讲了高阶函数的概念,并编写了一个简单的高阶函数: def add(x, y, f): return f(x) + f(y) 如果传入abs作为参数 ...

  3. python中“生成器”、“迭代器”、“闭包”、“装饰器”的深入理解

    python中"生成器"."迭代器"."闭包"."装饰器"的深入理解 一.生成器 1.生成器定义:在python中,一边 ...

  4. python --- Python中的callable 函数

    python --- Python中的callable 函数 转自: http://archive.cnblogs.com/a/1798319/ Python中的callable 函数 callabl ...

  5. python中使用zip函数出现<zip object at 0x02A9E418>

    在Python中使用zip函数,出现<zip object at 0x02A9E418>错误的原因是,你是用的是python2点多的版本,python3.0对python做了改动 zip方 ...

  6. [转载]python中multiprocessing.pool函数介绍

    原文地址:http://blog.sina.com.cn/s/blog_5fa432b40101kwpi.html 作者:龙峰 摘自:http://hi.baidu.com/xjtukanif/blo ...

  7. Python 中的isinstance函数

    解释: Python 中的isinstance函数,isinstance是Python中的一个内建函数 语法: isinstance(object, classinfo) 如果参数object是cla ...

  8. Python中的map()函数和reduce()函数的用法

    Python中的map()函数和reduce()函数的用法 这篇文章主要介绍了Python中的map()函数和reduce()函数的用法,代码基于Python2.x版本,需要的朋友可以参考下   Py ...

  9. python中multiprocessing.pool函数介绍_正在拉磨_新浪博客

    python中multiprocessing.pool函数介绍_正在拉磨_新浪博客     python中multiprocessing.pool函数介绍    (2010-06-10 03:46:5 ...

随机推荐

  1. Python测量时间,用time.time还是time.clock

    在计算机领域有多种时间.第一种称作CPU时间或执行时间,用于测量在执行一个程序时CPU所花费的时间.第二种称作挂钟时间,测量执行一个程序时的总时间.挂钟时间也被称作流逝时间或运行时间.与CPU时间相比 ...

  2. 2.Spring Cloud初相识--------Eureka服务注册与消费

    前言: 1.Eureka介绍: Spring Cloud Eureka,使用Netflix Eureka来实现服务注册与发现,它既包含了服务端组件,也包含了客户端组件,并且服务端与客户端均采用Java ...

  3. singnal 13 was raised

    在app运行过程中按下home键或者其他原因app被挂起,socket连接不会断开,服务器为了节省资源,在一段时间后会主动关闭这个连接.当玩家再次切回到游戏后,前端并不知道这个连接已经断开了,继续通过 ...

  4. ES6初识-Proxy和Reflect

    { let obj={ time:'2017-03-11', name:'net', _r:123 };   let monitor=new Proxy(obj,{ // 拦截对象属性的读取 get( ...

  5. DB设计工具——dbschema

      Preface       I've got a db design job about meeting room booking system last week.There're many s ...

  6. TP5 行为Behavior用法说明

    TP5 行为Behavior用法说明 无论是tp3还是在tp5中,行为都是一个非常重要的概念,关于太多的理论知识,就不多说了,不了解的请查看开发文档:TP5 行为概述 以下,就由代码来一步一步实现行为 ...

  7. MYSQL SQL高级查询技巧

    1.UNION,EXCEPT,INTERSECT运算符 A,UNION 运算符 UNION 运算符通过组合其他两个结果表(例如 TABLE1 和 TABLE2)并消去表中任何重复行而派生出一个结果表. ...

  8. jira安装说明

    阅读目录 1.1 jira说明 1.2 安装配置jira 1.3 web界面访问 1.4 创建第一个项目 1.5 参考文献 回到顶部 1.1 jira说明 JIRA是Atlassian公司出品的项目与 ...

  9. 【异常】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 ...

  10. PHP.29-TP框架商城应用实例-后台6-商品会员添加-价格、级别

    首先把需求分析搞清楚 主要实现两个功能 1.会员管理,设置成为会员的要求 2.添加商品时,可设置会员优惠价格 具体实现 1.建表[会员级别限定表p39_member_level{Id,级别名称,积分下 ...