【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等都是函数式编程的例子.在函数式编程中,函数也作为一个变量存在,对应 ...
随机推荐
- Linux系统——date命令
date命令 作用:用来显示或设定系统的日期与时间. 参数 -d<字符串>:显示字符串所指的日期与时间.字符串前后必须加上双引号: -s<字符串>:根据字符串来设置日期与时间. ...
- Linux系统——硬链接与软链接
文件属性软硬连接: 链接有两种,一种为硬链接(Hard Link),另一种为软链接或符号链接(Symbolic Link或Soft Link). 建立硬链接时,链接文件和被链接文件必须位于同一个文件系 ...
- CCF 权限查询(模拟)
试题编号: 201612-3 试题名称: 权限查询 时间限制: 1.0s 内存限制: 256.0MB 问题描述: 问题描述 授权 (authorization) 是各类业务系统不可缺少的组成部分,系统 ...
- Codeforces Round #112 (Div. 2) D. Beard Graph
地址:http://codeforces.com/problemset/problem/165/D 题目: D. Beard Graph time limit per test 4 seconds m ...
- Akka in action (第一章 介绍Akka)
在本章 概述Akka 了解Actors和Actor系统 Akka的适用范围 在第一章中,会介绍给你Akk的个方面,它能做什么,与现有的解决方案有那些不同.重点关注Akka有哪些功能和使用范围和强大的并 ...
- python中统计计数的几种方法
以下实例展示了 count() 方法的使用方法: 1 2 3 4 5 6 # !/usr/bin/python3 T = (123, 'Google', 'Runoob', 'Taobao', 1 ...
- C++中去掉string字符串中的\r\n等
string imagedata;imagedata = “dudau\r\ndadafca\r\n” CString Image; Image = imagedata.c_str(); Image. ...
- nuget发布自已的程序集
1.nuget注册并获取apikey 2.下载nuget.exe 3.设置apikey nuget setApiKey <apikey> 4.开发程序集 5.进入.csproj目录生成描述 ...
- Django学习笔记之ORM字段和字段参数
Object Relational Mapping(ORM) 一.ORM介绍 1. ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象 ...
- MVC中 关于退出按钮的写法
public ActionResult Logoff() { Session.Abandon(); Session.Clear(); FormsAuthentication.SignOut(); re ...