【Python】什么是闭包
文章转载自:点这里
在 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
中,这个引用环境被存储在一个cell
的tuple
中。你能够通过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__
参考###
- https://serholiu.com/python-closures
- http://blog.jobbole.com/66895/
- http://www.cnblogs.com/yuyan/archive/2012/04/21/2461673.html
【Python】什么是闭包的更多相关文章
- 说说Python中的闭包 - Closure
转载自https://segmentfault.com/a/1190000007321972 Python中的闭包不是一个一说就能明白的概念,但是随着你往学习的深入,无论如何你都需要去了解这么一个东西 ...
- 说说Python中的闭包
Python中的闭包不是一个一说就能明白的概念,但是随着你往学习的深入,无论如何你都需要去了解这么一个东西. 闭包的概念 我们尝试从概念上去理解一下闭包. 在一些语言中,在函数中可以(嵌套)定义另一个 ...
- Python中的闭包 - Closure
Python中的闭包不是一个一说就能明白的概念,但是随着你往学习的深入,无论如何你都需要去了解这么一个东西. 闭包的概念 我们尝试从概念上去理解一下闭包. 在一些语言中,在函数中可以(嵌套)定义另一个 ...
- 21.python中的闭包和装饰器
python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure). 以下说明主要针对 python ...
- 【转】python中的闭包
转自:http://www.cnblogs.com/ma6174/archive/2013/04/15/3022548.html python中的闭包 什么是闭包? 简单说,闭包就是根据不同的配置信息 ...
- Python 入门之 闭包
Python 入门之 闭包 1.闭包 (1)在嵌套函数内使用(非本层变量)和非全局变量就是闭包 (2)_ closure _ 判断是不是闭包 def func(): a = 1 def foo(): ...
- Python核心编程-闭包
百度搜了一下闭包的概念:简而言之,闭包的作用就是在外部函数执行完并返回后,闭包使得收机制不会收回函数所占用的资源,因为内部函数的执行需要依赖外函数中的变量.这是对闭包作用的非常直白的描述,不专业也不严 ...
- Python深入04 闭包
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 闭包(closure)是函数式编程的重要的语法结构.函数式编程是一种编程范式 (而 ...
- 轻松理解python中的闭包和装饰器 (下)
在 上篇 我们讲了python将函数做为返回值和闭包的概念,下面我们继续讲解函数做参数和装饰器,这个功能相当方便实用,可以极大地简化代码,就让我们go on吧! 能接受函数做参数的函数我们称之为高阶函 ...
- 轻松理解python中的闭包和装饰器(上)
继面向对象编程之后函数式编程逐渐火起来了,在python中也同样支持函数式编程,我们平时使用的map, reduce, filter等都是函数式编程的例子.在函数式编程中,函数也作为一个变量存在,对应 ...
随机推荐
- SVN遇到的问题和解决方法(后期还会继续更新)
1,smartsvn客户端(version客户端类似),一些.a文件无法识别,也就无法提交到svn! 解决办法如下: 在smartsvn客户端下面view->Ignored Files 勾选上就 ...
- 字符串的partition函数
partition函数 str1='sdga2a34'aa=str1.partition('a') print(aa) """ ('sdg', 'a', '2a34') ...
- 带宽、流量、CDN
1.啥叫带宽? 1.1 带宽的概念: 在网络中的带宽往往是指一个固定的时间内,能通过的最大位数据,即数据传输率.带宽是一个计量单位,用来计量单位时间内传输的数据量的多少. 1.2 计量单位: 这个数据 ...
- Django的orm练习---多表查询
Django的orm练习---多表查询 表关系如下 表结构 : from django.db import models # Create your models here. # 多对多-----&g ...
- spark[源码]-SparkEnv执行环境创建
sparkEnv概述 sparkEnv是spark的执行环境,其中包括众多与Executor执行相关的对象.在local模式下Driver会创建Executor,local-cluster部署模式或者 ...
- oracle安装完成后目录中不论有没有tnsnames.ora和listener.ora文件 PLSQL都能连上的问题解决方法
今天遇到这个问题了,发现listener.ora文件和tnsnames.ora文件在Net Work文件夹下没有,正常情况下安装完oracle或者是oracle Client是会有的,但是在Net M ...
- debug教程
名称 解释 格式 a (Assemble) 逐行汇编 a [address] c (Compare) 比较两内存块 c range address d (Dump) 内存16进制显示 d [addre ...
- jQuery中this 和 $(this)
var node = $('#id'); node.click(function(){ this.css('display','block'); //报错 this是一个html元素,不是jquer ...
- 延迟环境变量扩展(bat)
延迟环境变量扩展(bat) 之前遇到一些环境变量的问题,简单记录下 From:http://www.cnblogs.com/dongzhiquan/archive/2012/09/05/2671218 ...
- saltstack之nginx、php的配置
saltstack为nginx提供状态配置 1.创建nginx配置需要的目录 mkdir /srv/salt/prod/nginx mkdir /srv/salt/prod/nginx/files 2 ...