闭包的定义

  • 维基百科的定义:

在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体

在一些语言中,在函数中定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。运行时,一旦外部的函数被执行,一个闭包就形成了,闭包中包含了内部函数的代码,以及所需外部函数中的变量的引用。其中所引用的变量称作上值(upvalue)

闭包的用途

  1. 与函数内部变量创建关联,从而避免全局变量;
  2. 让这些内部变量的值始终保持在内存中。

实例

实例1

1
2
3
4
5
6
7
8
9
10
11
12
13
def makeInc(x):
def inc(y):
# 在 inc() 函数的定义里,x 是 “闭合” 的
return y + x
return inc # 返回动态函数inc
inc5 = makeInc(5) # 取得makeInc返回的动态函数
inc10 = makeInc(10) # 两个函数并与同一个 x 关联
inc5(5) # returns 10
inc10(5) # returns 15

说明

Python 中的闭包通过函数调用来创建。在上面的代码中,调用 makeInc 函数将为 x 变量创建一个关联,该变量在内部函数 inc 中被引用。每一次调用 makeInc 的时候都将创建一个该函数的实例,但每个实例具体引用的 x 的关联是不一样的。

实例2

这是一段用于为英语单词增加复数形式的代码:

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import re
def build_match_and_apply_functions(pattern, search, replace):
def matches_rule(word):
return re.search(pattern, word)
def apply_rule(word):
return re.sub(search, replace, word)
return (matches_rule, apply_rule) # 返回动态函数组成的元组
patterns = \
(
('[sxz]$', '$', 'es'),
('[^aeioudgkprt]h$', '$', 'es'),
('(qu|[^aeiou])y$', 'y$', 'ies'),
('$', '$', 's')
)
rules = [build_match_and_apply_functions(pattern, search, replace)
for (pattern, search, replace) in patterns]
def plural(noun):
for matches_rule, apply_rule in rules:
if matches_rule(noun):
return apply_rule(noun)

说明

build_match_and_apply_functions() 函数用于 动态创建其它函数 ,它返回的是两个函数对象组成的元组。

基本上,常量的创建工作都在创建应用函数过程中完成:它接受一个参数 (word),但 实际操作还加上了另外两个值 (search 和 replace),该两个值都在定义应用函数时进行设置。

匹配模式文件

上面的例子中,由于使用了闭包,匹配模式已经分离成一个单独的 patterns 元组。下一个逻辑步骤是将这些字符串放入一个单独的文件中,因此可独立于使用它们的代码来进行维护。

1
2
3
4
[sxz]$ $ es
[^aeioudgkprt]h$ $ es
[^aeiou]y$ y$ ies
$ $ s

下面看看如何使用该规则文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import re
def build_match_and_apply_functions(pattern, search, replace):
def matches_rule(word):
return re.search(pattern, word)
def apply_rule(word):
return re.sub(search, replace, word)
return (matches_rule, apply_rule)
rules = []
with open('plural4‐rules.txt', encoding='utf‐8') as pattern_file:
for line in pattern_file:
pattern, search, replace = line.split(None, 3)
rules.append(build_match_and_apply_functions(pattern, search, replace))
def plural(noun):
for matches_rule, apply_rule in rules:
if matches_rule(noun):
return apply_rule(noun)

此处的改进是将复数形式规则独立地放到了一份外部文件中,因此可独立于使用它的代码单独对规则进行维护。代码是代码,数据是数据,生活更美好。

Lambda表达式

lambda语句用于在运行时创建并返回新的函数对象。

示例一

1
2
3
4
5
6
7
8
9
#!/usr/bin/python
# Filename: lambda.py
def make_repeater(n):
return lambda s: s * n
twice = make_repeater(2)
print(twice('word'))
print(twice(5))

输出

1
2
3
$ python lambda.py
wordword
10

说明

在运行时我们利用函数make_repeater创建一个新的函数对象并返回它。其中一条lambda语句用于创建函数对象。

本质上这条lambda需要一个参数后跟一个相当于函数体的单表达式,这个表达式的值将成为函数的返回值。

示例二

利用lambda创建一个比较函数并将其提供给list.sort()

1
2
3
points = [ { 'x' : 2, 'y' : 3 }, { 'x' : 4, 'y' : 1 } ]
points.sort(key=lambda i : i['y'])
print(points)

输出

1
[{'x': 4, 'y': 1}, {'x': 2, 'y': 3}]

对象和闭包

受人尊敬的大师 Qc Na 和他的学生 Anton 走在一起。为了能和大师发起一次讨论,Anton 说道:”大师,我听说对象是个非常奇妙的东西,这是真的吗?“ 大师不屑的看了他的学生一眼,回答道:“蠢孩子 – 对象仅仅是可怜人的闭包。”

被责骂后,Anton 辞别了大师,回到了自己的小房间里,热情的学起闭包知识。 他认真的阅读了所有的“Lambda:XXX权威大全”系列文章以及相关资料,而且用Scheme编译器实现了一个小的以闭包为基础的面向对象的语言系统。他明白了很多事情,急切的想告诉他的老师自己取得的进步。

就在之后的一次跟大师的走路时,Anton 期望让大师对自己留下深刻印象,说到:”大师,我已经认真的学习了那个东西,现在我知道了对象真的是可怜人的闭包。“ 大师用手杖打了一顿 Anton, 说道:“你什么时候明白了?闭包是可怜人的对象。”

这回,Anton 终于开窍了。

深入阅读

Comments