python之嵌套函数与闭包

原文链接:http://yunjianfei.iteye.com/blog/2186092

嵌套函数

python是允许创建嵌套函数的,也就是说我们可以在函数内部定义一个函数,这些函数都遵循各自的作用域和生命周期规则。

 

Python代码   收藏代码
  1. #!/usr/bin/env python  
  2.   
  3. def outer():  
  4.     x = 1  
  5.     def inner():  
  6.         print x # 1  
  7.     inner() # 2  
  8.   
  9. outer()  

 输出结果:

 

1

 这个例子比普通的函数定义看起来复杂了一点,实际上都是很合理的。

 

1. #1的地方,python寻找名为x的local变量,在inner作用域内的locals中寻找不到,python就在外层作用域中寻找,其外层是outer函数。x是定义在outer作用域范围内的local变量。

2.  #2的地方,调用了inner函数。这里需要特别注意:inner也只是一个变量名,是遵循python的变量查找规则的(Python先在outer函数的作用域中寻找名为inner的local变量)

 

函数在python中是first-class对象

上面这句话看着有点抽象,简单点来说,在python中所有的东西都是对象,函数也是对象,看下面的代码:

 

Python代码   收藏代码
  1. #!/usr/bin/env python  
  2.   
  3. a = 1  
  4. print a.__class__  
  5. print issubclass(a.__class__, object) # all objects in Python inherit from a common baseclass  
  6.   
  7. def foo():  
  8.     pass  
  9.   
  10. print foo.__class__ # 1  
  11. print issubclass(foo.__class__, object)  

 输出结果:

<type 'int'>
True
<type 'function'>
True

 可以看到foo和变量a一样,都是顶级父类object的子类。a是一个int变量,foo是一个函数。

 

所以,函数没有什么特殊的,它和python里的其他东西一样,都属于对象,其父类是object。这意味着,

1. 函数和其他变量是一样,变量是可以传递和修改值的,函数也可以作为变量

2. 函数也可以作为函数的参数或者函数的返回值。

如下的例子:

 

Python代码   收藏代码
  1. #!/usr/bin/env python  
  2.   
  3. def add(x, y):   
  4.     return x + y   
  5. def sub(x, y):   
  6.     return x - y   
  7. def apply(func, x, y): # 1   
  8.     return func(x, y) # 2   
  9.   
  10. print apply(add, 21# 3  
  11.   
  12. print apply(sub, 21)  

 执行结果

 

3
1

 这个例子中,add和sub就是两个普通的函数对象。#1中,func这个参数用来接收函数变量(和其他参数接收变量一样)。在#2处,用来执行func参数传递进来的函数。在#3处,我们把add函数作为参数传递给apply,在apply中执行,并返回结果。

 

再看一个例子:

 

Python代码   收藏代码
  1. #!/usr/bin/env python  
  2.   
  3. def outer():  
  4.     def inner():  
  5.         print "Inside inner"  
  6.     return inner # 1  
  7.   
  8. foo = outer() #2  
  9.   
  10. print foo  
  11. foo()  

 输出结果:

 

<function inner at 0xda38140>
Inside inner

 1. 在#1处, 我们返回了inner变量(这个变量是函数标签)。

      注意:这里并没有去调用inner,调用的话是inner()

2. 在#2处,我们将outer函数返回的结果赋值给foo变量,foo返回的是inner函数的标签(注意输出结果),       概念类似c语言中的函数指针

3. 最后执行foo的时候,实际上执行的是inner。

 

以上的例子充分说明,函数完全可以看做是变量,可以把它赋值给其他变量,且有变量作用域、变量生存周期等。

 

闭包(Closures)

再来看一个例子:

 

Python代码   收藏代码
  1. #!/usr/bin/env python  
  2.   
  3. def outer():  
  4.     x = 1  
  5.     def inner():  
  6.         print x # 1  
  7.     return inner  
  8. foo = outer()  
  9. print foo.func_closure #2 doctest: +ELLIPSIS  
  10.   
  11. foo()  

 输出结果:

 

(<cell at 0x189da2f0: int object at 0x188b9d08>,)
1

 

在这个例子中,我们可以看到inner函数作为返回值被outer返回,然后存储在foo变量中,我们可以通过foo()来调用它。但是真的可以跑起来吗?让我们来关注一下作用域规则。

 

python里运行的东西,都按照作用域规则来运行。

1. x是outer函数里的local变量

2. 在#1处,inner打印x时,python在inner的locals中寻找x,找不到后再到外层作用域(即outer函数)中寻      找,找到后打印。

 

看起来一切OK,那么从变量生命周期(lifetime)的角度看,会发生什么呢:

1. x是outer的local变量,这意味着只有outer运行时,x才存在。那么按照python运行的模式,我们不能在         outer结束后再去调用inner。

2. 在我们调用inner的时候,x应该已经不存在了。应该发生一个运行时错误或者其他错误。

但是这一些都没有发生,inner函数依旧正常执行,打印了x。

 

Python支持一种特性叫做函数闭包(function closures):在非全局(global)作用域中定义inner函数(即嵌套函数)时,会记录下它的嵌套函数namespaces(嵌套函数作用域的locals),可以称作:定义时状态,可以通过func_closure 这个属性来获得inner函数的外层嵌套函数的namespaces。(如上例中#2,打印了func_closure ,里面保存了一个int对象,这个int对象就是x)

 

注意:每次调用outer函数时,inner函数都是新定义的。上面的例子中,x是固定的,所以每次调用inner函数的结果都一样。

 

下面我们看一个不同的例子:

 

C代码   收藏代码
  1. #!/usr/bin/env python  
  2.   
  3. def outer(x):  
  4.     def inner():  
  5.         print x # 1  
  6.     return inner  
  7. print1 = outer(1)  
  8. print2 = outer(2)  
  9. print print1.func_closure  
  10. print1()  
  11. print print2.func_closure  
  12. print2()  

 输出结果:

 

(<cell at 0x147d3328: int object at 0x146b2d08>,)
1
(<cell at 0x147d3360: int object at 0x146b2cf0>,)
2

 在这个例子中,我们能看到闭包实际上是记录了外层嵌套函数作用域中的local变量。通过这个例子,我们可以创建多个自定义函数。

 

思维扩展一下,上面这个例子,很容易让人想到面向对象编程:

1. outer是inner的构造器

2.x是inner的一个私有变量

 

这里主要是帮助读者理解闭包的概念,

 

下面附上几个链接,帮助更好的理解:

http://ynniv.com/blog/2007/08/closures-in-python.html

http://www.shutupandship.com/2012/01/python-closures-explained.html


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值