文章转载自:点这里

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

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

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

举个例子:

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

def generate_power_func(n):
print "id(n): %X" % id(n)
def nth_power(x):
return x**n
print "id(nth_power): %X" % id(nth_power)
return nth_power

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

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

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

>>> del generate_power_func
>>> raised_to_4(2)
16

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


_closure_ 属性和 cell 对象


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

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

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

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

2. 进一步加深闭包理解

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

来看一个简单的例子:

>>> def return_func_that_prints_s(s):
... def f():
... print s
... return f
...
>>> g = return_func_that_prints_s("Hello")
>>> h = return_func_that_prints_s("World")
>>> g()
Hello
>>> h()
World
>>> g is h
False
>>> h.__closure__
(,)
>>> print [str(c.cell_contents) for c in g.__closure__]
['Hello']
>>> print [str(c.cell_contents) for c in h.__closure__]
['World']

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

>>> def return_func_that_prints_list(z):
... def f():
... print z
... return f
...
>>> z = [1, 2]
>>> g = return_func_that_prints_list(z)
>>> g()
[1, 2]
>>> z.append(3)
>>> g()
[1, 2, 3]
>>> z = [1]
>>> g()
[1, 2, 3]

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

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

def dump_closure(f):
if hasattr(f, "__closure__") and f.__closure__ is not None:
print "- Dumping function closure for %s:" % f.__name__
for i, c in enumerate(f.__closure__):
print "-- cell %d = %s" % (i, c.cell_contents)
else:
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. Delphi APP 開發入門(四)簡易手電筒

    Delphi APP 開發入門(四)簡易手電筒 分享: Share on facebookShare on twitterShare on google_plusone_share   閲讀次數:32 ...

  2. nginx 413文件上报太大

    在nginx使用过程中,上传文件的过程中,通常需要设置nginx报文大小限制.避免出现413 Request Entity Too Large. 于是奇葩的问题被我们遇到了,详细配置请参考下面.我们的 ...

  3. Maven的plugins、pluginManagement和dependencies、dependencyManagement

    plugins和dependencies下边配的都是真实使用的. pluginManagement和dependencyManagement下边配的都只是做声明的,一般配置在顶级pom中. 参考链接: ...

  4. PHP秒杀系统全方位设计分析(一)

    秒杀系统特点人多商品少时间短流量高外挂机器[黄牛和非黄牛] 技术分析瞬间高并发的处理能力多层次的分布式处理能力人机交互与对抗[12306验证码图片] 技术选型分析Linux+Nginx+PHP+Mys ...

  5. IP查找所属网段

    最近同学接到阿里面试题 package io.guangsoft.analysis; /* 数据文件: 1.1.1.0/24,123 1.1.2.0/28,345 1.2.0.0/16,789 */ ...

  6. cl.exe 命令行编译sqlite3 sqlite3.dll及sqlite3.exe

    有点被宇宙最强的ide惯坏了,封装的太好,不能像gcc那样一步步了解其原理,其实强大的vs背后也有类似gcc的cl.exe 看到How To Compile SQLite http://sqlite. ...

  7. 20145322《Java程序设计》第2次实验报告

    20145322<Java程序设计>第2次实验报告 实验步骤与内容 一.实验内容 1.初步掌握单元测试和TDD 2.理解并掌握面向对象三要素:封装.继承.多态 3.初步掌握UML建模 4. ...

  8. parted 分区

    Linux下的GPT分区 GPT分区 这是另外一种分区,针对MBR分区,它有很多优点: (1)几乎突破了分区个数的限制. 在GPT分区表中最多可以支持128个主分区. (2)单个分区容量几乎没有限制. ...

  9. 爬虫之动态HTML处理(Selenium与PhantomJS )网站模拟登录

    #coding=utf-8from selenium import webdriverfrom selenium.webdriver.common.keys import Keysimport tim ...

  10. Win7旗舰版中的IIS配置asp.net 完美通过版,附代码 以及出现的 CS0016: 未能写入输出文件“c:\Windows\Microsoft.NET\Framework64\v2.0.50727\Temporary ASP.NET Files\root\8d57d

    先解决问题:“c:\Windows\Microsoft.NET\Framework64\v2.0.50727\Temporary ASP.NET Files\root\8d57d 图: 其他的解决方案 ...