返回函数,顾名思义,就是高阶函数可以把函数作为return值返回。与闭包的关系是:闭包需要以返回函数的形式实现。
一. 返回函数
比如我们有一个求和函数:
>>> def calc_sum(num_list):
s = 0
for i in num_list:
s += i
return s
>>> calc_sum([1,2,3,4])
当我们不需要立刻求和,而是后面根据需要再计算结果时,我们可以返回求和的函数,而不是直接返回计算结果。这就是返回函数。
'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
>>> 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()
很显然,这样能让我们根据需求,节省计算资源。
二. 闭包
在上面的例子中,我们在函数lazy_clac_sum中又定义了函数calc_sum,并且,内部函数calc_sum可以引用外部函数lazy_calc_sum的参数和局部变量,当lazy_calc_sum返回函数calc_sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”。
如果让定义更加清晰一些: 如果在一个内部函数里对在外部作用域(但不是在全局作用域)的变量进行引用,但不在全局作用域里,则这个内部函数就是一个闭包。
实际上,闭包的用处/优点有两条:
- 从函数外可以读取函数内部的变量
- 让这些变量的值始终保持在内存中(也可以理解为保留当前运行环境)
下面例子是,我们创建了一个下载download函数,然后下载次数一直存储在内存中。
'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
>>> 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个常见错误:
- 尝试在闭包中改变外部作用域的局部变量
'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
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)
- 误以为返回的函数就已执行,对执行结果误判
直接举例子说明:
'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
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()
>>> f2()
>>> f3()
全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:
'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
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()
>>> f2()
>>> f3()