Python 浅谈闭包

闭包的介绍

闭包是一种编程理念,支持将函数当成对象使用的编程语言,一般都支持闭包,比如Python, JavaScript。

在维基百科上,对闭包有如下定义:

在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。

用通俗一点的话来说,就是函数中嵌套了另一个函数(称为内部函数),这个内部函数使用了外部函数的参数、以及函数中定义的变量,并且内部函数被当成对象返回,这样就形成了闭包。

闭包的形成条件

通过闭包的介绍,我们可以得知闭包的形成条件:

  • 存在函数嵌套(函数里面再定义函数)
  • 内部函数使用了外部函数的变量(还包括外部函数的参数)
  • 外部函数返回了内部函数

闭包的示例

下面使用Python写一个简单的闭包示例。需求是这样的:有一个数据保存的业务,保存的介质可能是文件,也可能是数据库等其他方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14

def save_data(type):
num = 0
def wapper(content):
print(str.format("数据保存至【{target}】,保存内容为:{content}",target=type,content=content))
nonlocal num
num += 1
print(str.format("今日已保存数据条数:{0}" ,num))
return wapper


save_to_database = save_data("数据库")
save_to_database("一条订单记录")
save_to_database("个人信息变更")

执行上面脚本结果如下:

1
2
3
4
数据保存至【数据库】,保存内容为:一条订单记录
今日已保存数据条数:1
数据保存至【数据库】,保存内容为:个人信息变更
今日已保存数据条数:2

闭包的作用

通过上面的简单示例,容易看出闭包的一个优点是复用性,普通的函数调用完就会释放资源了,但使用闭包的情况下,将一个函数的功能蕴含在一个对象中,这个对象通常不会被回收,也就是将函数的逻辑功持久化到了内存中,后续可以多次复用该函数逻辑。

扩展:Java中的闭包

Java是面向对象编程的,所以不存在函数嵌套的这种情况,但Java的类是可以嵌套的,可以用类的嵌套来实现类似的思想。
下面是用Java实现前文中保存数据的例子。

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
28
29
30
31
32
33
34
35
36
37
38
39
40

/**
* @author liaosi
*/
public class SaveData {

private String target;

private InnerClass innerClass;

private int num = 0;

public SaveData(String target) {
this.target = target;
this.innerClass = new InnerClass();
}

private class InnerClass {

public void saveContent(String content) {
System.out.println("数据保存至【" + target + "】,保存内容为:" + content);
num++;
System.out.println("今日已保存数据条数:" + num);
}

}


public void save(String content) {
innerClass.saveContent(content);
}


public static void main(String[] args) {
SaveData saveToDatabase = new SaveData("数据库");
saveToDatabase.save("一条订单记录");
saveToDatabase.save("个人信息变更");
}

}

在 Java8 中,引入了 lambda 表达式和函数式接口,函数式接口只有一个方法,意味着一个接口只用来实现一个单一的功能,这样的接口就有点像函数的意味了。前面讲到,闭包的思想是把一个逻辑功能封装到对象里,所以我们可以创建一个函数式接口的实例,将接口的功能封装在对象里,我理解这也算是在Java中闭包思想的一种实现方式。

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
28
29

import java.util.function.Consumer;

/**
* @author liaosi
*/
public class FunctionalExample {


public static void main(String[] args) {

Consumer<String> saveToHdfs = new Consumer<String>() {

private int num = 0;

@Override
public void accept(String content) {
System.out.println("数据保存至【HDFS】,保存内容为:" + content);
num++;
System.out.println("今日已保存数据条数:" + num);
}
};

saveToHdfs.accept("一条订单记录");
saveToHdfs.accept("个人信息变更");
}


}

------ 本文完 ------