Chinaunix首页 | 论坛 | 博客
  • 博客访问: 82484
  • 博文数量: 18
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 321
  • 用 户 组: 普通用户
  • 注册时间: 2013-07-30 21:09
文章分类

全部博文(18)

文章存档

2015年(3)

2014年(9)

2013年(6)

我的朋友

分类: Python/Ruby

2015-02-09 17:48:23

闭包(closures)这一概念在许多脚本语言,比如javascript,python,lua中普遍存在,对于长期用c/c++编程的程序员来讲,闭包的概念不是特别容易理解,也不清楚闭包的应用场景。确实,闭包的概念是面向过程编程的产物,其本质含义是一个函数和某个数据绑定在一起,数据的生命周期和函数的生命周期一样长。从上面这段话中,隐隐约约感觉到了什么?下面我们来看一下比较正式的闭包定义,并看一个简单的闭包例子。
python闭包定义:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure).

  1 #!/usr/bin/env python
  2 
  3 def fun(m):
  4     def add(n):
  5         return m + n
  6     return add
  7 
  8 f1 = fun(10)
  9 print f1(1) #输出11
 10 print f1(2) #输出12
 11 print f1(3) #输出13
 12 
 13 f2 = fun(20)
 14 print f2(1) #输出21
 15 print f2(2) #输出22
 16 print f2(3) #输出23

闭包是定义在一个函数内部的函数,该函数引用了其外层函数的局部变量或型参变量,然后外层函数将闭包函数做为返回值返回。外面的调用方可以直接调用闭包体(比如上述代码中的f1),外层函数的局部变量(比如fun中的局部变量m)都不会消失,直到闭包体消亡。另外一个特点是定义不同的闭包体的时候,比如f1和f2,两者之间的局部变量是互相不干预的,是各自独立的一份。
根据上述例子,我们很容易将闭包跟C++的类、对象、成员函数、成员变量一一对应起来,闭包和面向对象的类本质上属于同一个思想的东西。为了说明这一点,我们举一个更加复杂的例子:
  1 #!/usr/bin/env python                                                     
  2 def fun(m):                                                               
  3     n = [1]                                                               
  4     def inc():                                                            
  5         k = m[0] + n[0]                                                   
  6         n[0] += 1                                                         
  7         m[0] += 1                                                         
  8         print k                                                           
  9         
 10     def sub():                                                            
 11         n[0] -= 1                                                         
 12         m[0] -= 1                                                         
 13     
 14     return inc, sub                                                       
 15         
 16 f_inc, f_sub = fun([2])                                                   
 17         
 18 times = 0
 19 while True:                                                               
 20     f_inc()                                                               
 21     times += 1
 22     if times == 10: break                                                 
 23         
 24 times = 0
 25 while True:                                                               
 26     f_sub()                                                               
 27     times += 1                                                            
 28     if times == 10: break                                                 
 29     
 30 times = 0                                                                 
 31 while True:                                                               
 32     f_inc()                                                               
 33     times += 1                                                            
 34     if times == 10: break          

fun函数返回两个闭包函数f_inc及f_sub,这两个闭包函数引用了fun里面的局部变量m及n。从面向对象的观点来看,可以将fun理解为一个类,f_inc及f_sub理解为类的成员函数,fun里面的局部变量m,n可以理解为类的成员变量。对于函数fun的调用,比如fun([2]),可以理解为类的实例化。从这些观点来理解闭包,就非常容易理解闭包的各种特性了。为了方便大家理解,大家可以参考下面这段c++代码,感觉闭包和C++的类、对象概念是不是完全一致?
  1 #include
  2 
  3 class CFun
  4 {
  5 public:
  6     CFun(int m)
  7     {
  8         _n = 1;
  9         _m = m;
 10     }
 11 
 12 public:
 13     void f_inc()
 14     {
 15         int k = _m + _n;
 16         _n++;
 17         _m++;
 18         printf("%d\r\n", k);
 19     }
 20 
 21     void f_sub()
 22     {
 23         _n--;
 24         _m--;
 25     }
 26 
 27 private:
 28     int _n;
 29     int _m;
 30 
 31 };
 33 int main()
 34 {
 35     CFun fun(2);
 36     int times = 0;
 37     while(times < 10)
 38     {
 39         fun.f_inc();
 40         times++;
 41     }
 42 
 43     times = 0;
 44     while(times < 10)
 45     {
 46         fun.f_sub();
 47         times++;
 48     }
 49 
 50     times = 0;
 51     while(times < 10)
 52     {
 53         fun.f_inc();
 54         times++;
 55     }
 56 
 57     return 0;
 58 }

根据闭包的特性,可以看出其比较适合用于回调函数。因为该回调函数天然和一堆数据绑定在一起,可以省去大量的数据存储、读取工作。试想一下再面向过程编程中,若没有闭包,在设计回调函数中必然有一块数据需要单独存储,在回调时再将这块数据找出来,然后塞给这个回调函数。有了闭包后再这块可以简单很多。但在面向对象编程中,闭包基本很难发挥什么特点,对于回调我们可以设计虚函数等方式来实现数据和函数的绑定。
下面再来简单分析一下闭包的实现,整个闭包的实现和脚本语言的内存回收机制是密不可分的。对于上面的例子,可以简单理解为当 f_inc, f_sub = fun([2]) 执行时,fun里面的局部变量都被开辟,且引用计数都变为了3(f_inc,f_sub,fun都取得了一个),当执行完时,虽然fun消亡,引用计数减一,但局部变量的引用计数依旧为2,不会被系统收回。当f_inc和f_sub被定义时,其作用域链已经被确定,如下图所示。当fun([2])被执行时,相当于确定了一个执行环境,作用域链上所有的变量都会被初始化。整个概念跟类的定义及类的实例化一模一样,因此采用面向对象的观念来理解闭包,就非常容易想清楚其背后的实现原理及应用场景。


参考资料:

http://blog.csdn.net/marty_fu/article/details/7679297












阅读(1228) | 评论(0) | 转发(0) |
0

上一篇:linux softirq

下一篇:漫谈GUN与Linux历史

给主人留下些什么吧!~~