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 ...
随机推荐
- js图片库 案例
事件处理函数:事件处理函数的作用是,在特定事件发生时调用特定的JavaScript代码.本例中想要在用户点击某个链接的时候触发一个动作,所以需要使用onclick事件处理函数. 添加事件处理函数的语法 ...
- IDEA 安装配置及操作总结(新手必看)
Jetbrains官网下载IDEA15 我们在浏览器输入网址https://www.jetbrains.com/.选择相应的系统版本,下载最新版本的IDEA15,Windows系统双击安装文件,根据界 ...
- 写给iOS小白的MVVM教程(序)
这几天,需要重构下部分代码,这里简要记录下.但是涉及的技术要点还是很多,所以分为多个篇章叙述.此教程来源于,并将于应用于实践,不做过多的概念性阐释和争论.每个篇章都会附上实际的可执行的代码.因涉及的技 ...
- 2017年10月26日 git上传文件失败的文件
最近几天因为项目要用git,于是学习了一下git.今天上传项目到码云的时候,却发现总有一些文件夹上传不上去,git 也显示everything is update.找了一圈办法,都没有用,最后突然发现 ...
- 图的m着色
图的m着色 #include <bits/stdc++.h> using namespace std; int n, k, m, ans; struct node{ int m, colo ...
- 在github上查找star最多的项目
如何在github上查找star最多的项目 在search中输入stars:>1 就可以查找所有有star的项目,然后右上角根据自己的需要筛选 当我输入stars:>10000的时候,就会 ...
- java 计算数学表达式及执行脚本语言
java SE6中对常用的脚本语言做了支持. 可供使用者在java代码中执行脚本语言,还可以利用get("key"),put("key","value ...
- Shell学习——数组
1.普通数组:只能用整数作为索引1.1.赋值[root@client02 ~]# array[0]=test1[root@client02 ~]# array[1]=test2[root@client ...
- Shell学习——数值运算
在Bash shell中,可以利用let.(( )).[]执行基本的算术操作,在高级操作时,使用expr和bc两个工具1.let[root@client02 ~]# no1=4[root@client ...
- Aizu:2200-Mr. Rito Post Office
快递 Time limit 8000 ms Memory limit 131072 kB Problem Description 你是某个岛国(ACM-ICPC Japan)上的一个苦逼程序员,你有一 ...