设为首页 收藏本站
查看: 506|回复: 0

[经验分享] python之嵌套函数与闭包

[复制链接]

尚未签到

发表于 2017-4-29 12:12:45 | 显示全部楼层 |阅读模式
函数基础
  关于函数基础,可以查看我的另一篇博客:http://yunjianfei.iteye.com/blog/2186064
  与本文相关的,有两个重要概念:变量作用域(scope)与生命周期(lifetime
  如果对于这两个概念有所疑惑,可以先去了解一下。

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

#!/usr/bin/env python
def outer():
x = 1
def inner():
print x # 1
inner() # 2
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中所有的东西都是对象,函数也是对象,看下面的代码:

#!/usr/bin/env python
a = 1
print a.__class__
print issubclass(a.__class__, object) # all objects in Python inherit from a common baseclass
def foo():
pass
print foo.__class__ # 1
print issubclass(foo.__class__, object)
  输出结果:

<type 'int'>
True
<type 'function'>
True
  可以看到foo和变量a一样,都是顶级父类object的子类。a是一个int变量,foo是一个函数。
  所以,函数没有什么特殊的,它和python里的其他东西一样,都属于对象,其父类是object。这意味着,
  1. 函数和其他变量是一样,变量是可以传递和修改值的,函数也可以作为变量
  2. 函数也可以作为函数的参数或者函数的返回值。
  如下的例子:

#!/usr/bin/env python
def add(x, y):
return x + y
def sub(x, y):
return x - y
def apply(func, x, y): # 1
return func(x, y) # 2
print apply(add, 2, 1) # 3
print apply(sub, 2, 1)
  执行结果

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

#!/usr/bin/env python
def outer():
def inner():
print "Inside inner"
return inner # 1
foo = outer() #2
print foo
foo()
  输出结果:

<function inner at 0xda38140>
Inside inner
  1. 在#1处, 我们返回了inner变量(这个变量是函数标签)。
  注意:这里并没有去调用inner,调用的话是inner()
  2. 在#2处,我们将outer函数返回的结果赋值给foo变量,foo返回的是inner函数的标签(注意输出结果),       概念类似c语言中的函数指针
  3. 最后执行foo的时候,实际上执行的是inner。
  以上的例子充分说明,函数完全可以看做是变量,可以把它赋值给其他变量,且有变量作用域、变量生存周期等。

闭包(Closures)
  再来看一个例子:

#!/usr/bin/env python
def outer():
x = 1
def inner():
print x # 1
return inner
foo = outer()
print foo.func_closure #2 doctest: +ELLIPSIS
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函数的结果都一样。
  下面我们看一个不同的例子:

#!/usr/bin/env python
def outer(x):
def inner():
print x # 1
return inner
print1 = outer(1)
print2 = outer(2)
print print1.func_closure
print1()
print print2.func_closure
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

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-370766-1-1.html 上篇帖子: 在Mac OSX系统中搭建Python集成开发环境 下篇帖子: 谈 Python 的中文编码处理
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表