文章转载自:点这里

在 Python 中很多教材都没有提及什么是闭包,但在定义一个 Decorator 时,就已经用到闭包了。如果不理解什么是闭包,则不可能清晰掌握Decorator 装饰器。

要形成闭包首先得有一个嵌套的函数,即函数中定义了另一个函数,闭包则是一个集合,它包括了外部函数的局部变量,这些局部变量在外部函数返回后也继续存在,并能被内部函数引用。

1. 先简单理解一下什么是闭包

举个例子:

这是个经常使用到的例子,定义一个函数 generate_power_func,它返回另一个函数,现在闭包形成的条件已经达到。

  1. def generate_power_func(n):
  2. print "id(n): %X" % id(n)
  3. def nth_power(x):
  4. return x**n
  5. print "id(nth_power): %X" % id(nth_power)
  6. return nth_power

对于内部函数 nth_power,它能引用到外部函数的局部变量 n,而且即使 generate_power_func 已经返回。把这种现象就称为闭包。具体使用一下。

  1. >>> raised_to_4 = generate_power_func(4)
  2. id(n): 246F770
  3. id(nth_power): 2C090C8
  4. >>> repr(raised_to_4)
  5. ' '

从结果可以看出,当 generate_power_func(4) 执行后, 创建和返回了 nth_power 这个函数对象,内存地址是 0x2C090C8,并且发现 raised_to_4 和它的内存地址相同,即 raised_to_4 只是这个函数对象的一个引用。先在全局命名空间中删除 generate_power_func,再试试会出现什么结果。

  1. >>> del generate_power_func
  2. >>> raised_to_4(2)
  3. 16

啊哈,居然没出现错误, nth_power 是怎么知道 n 的值是 4,而且现在 在 generate_power_func 甚至都不在这个命名空间了。对,这就是闭包的作用,外部函数的局部变量可以被内部函数引用,即使外部函数已经返回了。


_closure_ 属性和 cell 对象


现在知道闭包是怎么一回事了,那就到看看闭包到底是怎么回事的时候了。

Python 中函数也是对象,所以函数也有很多属性,和闭包相关的就是 __closure__ 属性。__closure__ 属性定义的是一个包含 cell 对象的元组,其中元组中的每一个 cell 对象用来保存作用域中变量的值

  1. >>> raised_to_4.__closure__
  2. (,)
  3. >>> type(raised_to_4.__closure__[0])>>> raised_to_4.__closure__[0].cell_contents
  4. 4

就如刚才所说,在 raised_to_4__closure__ 属性中有外部函数变量 n 的引用,通过内存地址可以发现,引用的都是同一个 n。如果没用形成闭包,则 __closure__ 属性为 None。对于 Python 具体是如何实现闭包的,可以查看 Python闭包详解,它通过分析 Python 字节码来讲述闭包的实现。

2. 进一步加深闭包理解

一个函数闭包是一个函数和一个引用集合的组合,这个引用集合指向这个函数被定义的作用域的变量。后者通常指向一个引用环境(referencing environment),这使得函数能够在它被定义的区域之外执行。在Python中,这个引用环境被存储在一个celltuple中。你能够通过func_closure或Python 3中的__closure__属性访问它。要铭记的一点是引用及是引用,而不是对象的深度拷贝。当然了,对于不可变对象而言,这并不是问题,然而对可变对象(list)这点就必须注意,随后会有一个例子说明。请注意函数在定义的地方也有__globals__字段来存储全局引用环境。

来看一个简单的例子:

  1. >>> def return_func_that_prints_s(s):
  2. ... def f():
  3. ... print s
  4. ... return f
  5. ...
  6. >>> g = return_func_that_prints_s("Hello")
  7. >>> h = return_func_that_prints_s("World")
  8. >>> g()
  9. Hello
  10. >>> h()
  11. World
  12. >>> g is h
  13. False
  14. >>> h.__closure__
  15. (,)
  16. >>> print [str(c.cell_contents) for c in g.__closure__]
  17. ['Hello']
  18. >>> print [str(c.cell_contents) for c in h.__closure__]
  19. ['World']

一个稍复杂的例子。确保明白为什么会这么执行。

  1. >>> def return_func_that_prints_list(z):
  2. ... def f():
  3. ... print z
  4. ... return f
  5. ...
  6. >>> z = [1, 2]
  7. >>> g = return_func_that_prints_list(z)
  8. >>> g()
  9. [1, 2]
  10. >>> z.append(3)
  11. >>> g()
  12. [1, 2, 3]
  13. >>> z = [1]
  14. >>> g()
  15. [1, 2, 3]

【译者】:z.append(3)时,g()内部的引用和z仍然指向一个变量,而z=1之后,两者就不再指向一个变量了。

最后,来看看代码中使用到的dump_closure方法的定义。

  1. def dump_closure(f):
  2. if hasattr(f, "__closure__") and f.__closure__ is not None:
  3. print "- Dumping function closure for %s:" % f.__name__
  4. for i, c in enumerate(f.__closure__):
  5. print "-- cell %d = %s" % (i, c.cell_contents)
  6. else:
  7. print " - %s has no closure!" % f.__name__

参考###

【Python】什么是闭包的更多相关文章

  1. 说说Python中的闭包 - Closure

    转载自https://segmentfault.com/a/1190000007321972 Python中的闭包不是一个一说就能明白的概念,但是随着你往学习的深入,无论如何你都需要去了解这么一个东西 ...

  2. 说说Python中的闭包

    Python中的闭包不是一个一说就能明白的概念,但是随着你往学习的深入,无论如何你都需要去了解这么一个东西. 闭包的概念 我们尝试从概念上去理解一下闭包. 在一些语言中,在函数中可以(嵌套)定义另一个 ...

  3. Python中的闭包 - Closure

    Python中的闭包不是一个一说就能明白的概念,但是随着你往学习的深入,无论如何你都需要去了解这么一个东西. 闭包的概念 我们尝试从概念上去理解一下闭包. 在一些语言中,在函数中可以(嵌套)定义另一个 ...

  4. 21.python中的闭包和装饰器

    python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure). 以下说明主要针对 python ...

  5. 【转】python中的闭包

    转自:http://www.cnblogs.com/ma6174/archive/2013/04/15/3022548.html python中的闭包 什么是闭包? 简单说,闭包就是根据不同的配置信息 ...

  6. Python 入门之 闭包

    Python 入门之 闭包 1.闭包 (1)在嵌套函数内使用(非本层变量)和非全局变量就是闭包 (2)_ closure _ 判断是不是闭包 def func(): a = 1 def foo(): ...

  7. Python核心编程-闭包

    百度搜了一下闭包的概念:简而言之,闭包的作用就是在外部函数执行完并返回后,闭包使得收机制不会收回函数所占用的资源,因为内部函数的执行需要依赖外函数中的变量.这是对闭包作用的非常直白的描述,不专业也不严 ...

  8. Python深入04 闭包

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 闭包(closure)是函数式编程的重要的语法结构.函数式编程是一种编程范式 (而 ...

  9. 轻松理解python中的闭包和装饰器 (下)

    在 上篇 我们讲了python将函数做为返回值和闭包的概念,下面我们继续讲解函数做参数和装饰器,这个功能相当方便实用,可以极大地简化代码,就让我们go on吧! 能接受函数做参数的函数我们称之为高阶函 ...

  10. 轻松理解python中的闭包和装饰器(上)

    继面向对象编程之后函数式编程逐渐火起来了,在python中也同样支持函数式编程,我们平时使用的map, reduce, filter等都是函数式编程的例子.在函数式编程中,函数也作为一个变量存在,对应 ...

随机推荐

  1. exec-maven-plugin配置及使用

    背景: 如果你想在项maven生命周期内,运行一段java代码,或者一段独立的程序,或者说我们所指的预执行,初始化某些值,生成某些不能预先生成的文件.那么这样我们就可以使用exec-maven-plu ...

  2. 定时任务之elastic-job概述

    定时任务有哪些? Timer定时器 ScheduledExecutorService Spring自带的@Scheduled Quartz定时任务 当当elastic job定时任务 Quartz实现 ...

  3. windows 常用cmd命令

    为了减少使用鼠标的频次,熟记一些常用应用的快捷键与系统本身常用的命令是必须的,以下记录一些常用的windows系统命令. 查看网络端口占用情况 :netstat -ano | findstr 8080 ...

  4. zookeeper No route to host

    2017-10-12 07:25:59,270 [myid:1] - WARN [QuorumPeer[myid=1]/0:0:0:0:0:0:0:0:2181:QuorumCnxManager@36 ...

  5. Web服务器文件传输程序客户端程序实现

    1. 客户端程序--主函数 客户端主程序的流程图如下: 主程序主要是分析输入的命令,根据不同命令调用不同的函数处理或者进行出错处理,函数代码如下: #include "common.h&qu ...

  6. 伪类 :after 清除浮动的原理和方法

    浮动元素容器的clearing问题1. 问题的由来有这样一种情形:在一个容器(container)中,有两个浮动的子元素.<div>        <div style=" ...

  7. PHP 日期格式中 Y与y

    大写Y效果: $nowtime = time(); $nowtime=date("Y-m-d",$nowtime); 结果: 2015-10-24 小写y效果: $nowtime ...

  8. NOIP2019前的训练记录

    \(April\):肛多项式,学\(FWT\)一个小时无果后背了六个公式,看来证明又得咕很久了

  9. DNS ARP地址解析原理

    ARP是地址解析协议 主机A与主机B之间如果要进行数据间的传输,需要获取对方的IP与物理地址(MAC),在只清楚ip的情况下,主机A向主机B请求链接,会先查找ARP高速缓存表,是否存在对应的主机B的i ...

  10. Thinkphp5.0实战开发一------命名空间详解

    序言 ThinkPHP是一个快速.兼容而且简单的轻量级国产PHP开发框架,使用ThinkPHP框架可以极大简化我们的开发过程,节省时间.这个专题我将记录自己学习使用ThinkPHP5.0的进行实战开发 ...