1、什么是闭包

在介绍闭包概念前,我们先来看一段简短的代码

def sum_calc(*args):
def wrapper():
sum = 0
for n in args:
sum += n;
return sum
return wrapper

很显然,这段代码定义了一个名为sum_calc的函数,但和定义的普通函数不同的是这个函数体的内部又定义了一个名为wrapper的函数,并且sum_calc函数的返回值是内部定义wrapper函数。

现在我们开始来调用sum_calc函数,看看会出现哪些有趣的事情

>>> f = sum_calc(1, 2, 3, 4, 5)
>>> f
<function sum_calc.<locals>.wrapper at 0x0000025693AC2D30>
>>> f()
15

从运行结果来看,当我们调用sum_calc时,返回的并不是求和结果,而是内部定义的求和函数。

继续调用sum_calc返回函数时,才真正计算出求和的结果。

当我们继续调用一次sum_calc,传入相同参数

>>> f1 = sum_calc(1, 2, 3, 4, 5)
>>> f2 = sum_calc(1, 2, 3, 4, 5)
>>> f1 == f2
False
>>> f1()
15
>>> f2()
15

每次调用sum_calc,即使传入相同的参数,两次返回的对象不同,且f1()和f2()的结果互不影响。

在这个例子中,我们在sum_calc函数中定义的wrapper函数,wrapper函数可以引用外部函数sum_calc的参数和局部变量,当sum_calc返回函数wrapper时,相关参数保存在返回的函数中,可以被返回的函数继续使用,我们把这种情况称为“闭包”。

参考维基百科,对“闭包”进行更严谨的解释:

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

2、闭包陷阱

def my_fun():
fs = []
for i in range(0, 3):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()

在上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都放在列表中返回。

我们可能认为,f1()、f2()、f3()结果是0、1、4,但实际结果是4

>>> f1()
4
>>> f2()
4
>>> f3()
4

这个例子可以说是典型的错误使用闭包的案例,my_fun返回的并不是一个闭包函数,而是一个包含三个闭包函数的一个列表。

在返回闭包列表fs之前for循环的变量的值已经发生改变了,循环内闭包函数仅声明了自己计算方式,并不会立即使用当前i的值进行计算。只有在真正调用闭包函数时,才会真正的执行闭包函数内的计算,而此时存放的i的值已经是2,所以f1()、f2()、f3()的结果是4而不是我在之前期待的0、1、4。

经过上面的分析,我们得出下面一个重要的经验:返回闭包中不要引用任何循环变量,或者后续会发生变化的变量。

正确写法

def my_fun(*args):
L = []
for i in range(3):
def wrapper(_i = i):
return _i * _i
L.append(wrapper)
return L

3、闭包实现机制

闭包比普通的函数多了一个 __closure__ 属性,该属性记录着自由变量内容。当闭包被调用时,系统就会根据该地址找到对应的自由变量,完成整体的函数调用

还以 sum_calc() 为例,当其被调用时,可以通过 __closure__ 属性获取自由变量(也就是程序中的 args参数)内容,例如:

def sum_calc(*args):
def wrapper():
sum = 0
for n in args:
sum += n;
return sum
return wrapper

输出结果:

>>> f1 = sum_calc(1, 2, 3, 4, 5)
>>> f1.__closure__
(<cell at 0x0000025693A5E4C0: tuple object at 0x0000025693AE5400>,)

>>> f1.__closure__[0].cell_contents
(1, 2, 3, 4, 5)

可以看到,显示的内容是一个tuple类型,这就是f1中自由变量args的值。还可以看到,__closure__ 属性的类型是一个元组,这表明闭包可以支持多个自由变量的形式。

浅析Python闭包的更多相关文章

  1. 浅析Python装饰器

    1.什么是装饰器 在介绍装饰器之前,我们先来思考一个问题:使用Python语言进行程序设计时,如果我们想扩展一个函数的功能,一般会怎么做呢? 比如,有一个名为print_info函数,当前该函数内只做 ...

  2. Python 闭包

    什么是闭包? 闭包(closure)是词法闭包(lexical closure)的简称.闭包不是新奇的概念,而是早在高级程序语言开始发展的年代就已产生. 对闭包的理解大致分为两类,将闭包视为函数或者是 ...

  3. Python闭包与函数对象

    1. Python闭包是什么 在python中有函数闭包的概念,这个概念是什么意思呢,查看Wikipedia的说明如下: “ In programming languages, closures (a ...

  4. Python闭包及装饰器

    Python闭包 先看一个例子: def outer(x): def inner(y): return x+y return innder add = outer(8) print add(6) 我们 ...

  5. Python闭包详解

    Python闭包详解 1 快速预览 以下是一段简单的闭包代码示例: def foo(): m=3 n=5 def bar(): a=4 return m+n+a return bar >> ...

  6. Python闭包及其作用域

    Python闭包及其作用域 关于Python作用域的知识在python作用域有相应的笔记,这个笔记是关于Python闭包及其作用域的详细的笔记 如果在一个内部函数里,对一个外部作用域(但不是全局作用域 ...

  7. Python 闭包小记

    闭包就是能够读取其他函数内部变量的函数.例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“.在本质上,闭包是将函数内部和函数外部连接起来 ...

  8. 理解Python闭包概念

    闭包并不只是一个python中的概念,在函数式编程语言中应用较为广泛.理解python中的闭包一方面是能够正确的使用闭包,另一方面可以好好体会和思考闭包的设计思想. 1.概念介绍 首先看一下维基上对闭 ...

  9. Python闭包举例

    Python闭包的条件: 1.函数嵌套.在外部函数内,定义内部函数. 2.参数传递.外部函数的局部变量,作为内部函数参数. 3.返回函数.外部函数的返回值,为内部函数. 举例如下: def line_ ...

随机推荐

  1. 如何让文科生5分钟写上Python

    序言 这篇文章是转型后发表的第一篇文章,有必要先聊一下我的写文计划. 串行文章和并行文章 我会按照发文顺序分为串行文章和并行文章.Python 语言本身的内容,我会按照入门.进阶.原理的顺序依次展开. ...

  2. CSS色调旋转滤镜

    一 关于filter 首先看一下官方对于CSS的filter属性的定义: CSS属性将模糊或颜色偏移等图形效果应用于元素.滤镜通常用于调整图像,背景和边框的渲染. 本文主要讲的是filter中的一个属 ...

  3. 「刷题笔记」AC自动机

    自动AC机 Keywords Research 板子题,同luoguP3808,不过是多测. 然后多测不清空,\(MLE\)两行泪. 板子放一下 #include<bits/stdc++.h&g ...

  4. Django 多对多表的三种创建方式

    第一种: class Book(models.Model): name = models.CharField(max_length=32) # 第一种自动创建 authors = models.Man ...

  5. Centos7配置阿里epel源|yum源

    这一步非常重要.重要.重要.在这解释一下源的概念,打个比方如果手机想获取一个软件,可以选择很多途径,如华为的华为商店,小米的应用商店,苹果的App store,源就相当于各种手机获取软件的商店.因为国 ...

  6. Codeforces Round #688(Div 2) D. Checkpoints

    思路 第一步,先推导1,0,0,--,0,就是1后面跟了n-1个0的时候 所需要的期望步数 封闭式推导 \(f_n\)代表从n关开始直接通关需要的步数的期望 n为1的情况,即就只有一个1 \(f_1= ...

  7. 在Python实现print标准输出sys.stdout、stderr重定向及捕获的简单办法

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 Python中的标准输出和错误输出由sys模块的stdout.stde ...

  8. 第11.6节 Python正则表达式的字符串开头匹配模式及元字符“^”(插入符、脱字符)功能介绍

    符号"^"为插入符,也称为脱字符,在Python中脱字符表示匹配字符串的开头,即字符串的开头满足匹配模式的要求.这个功能有点类似搜索函数match,只是这是通过搜索模式来指定,而m ...

  9. python接口自动化测试框架(post提交添加变量)

    1.python接口测试框架包含哪几部分 数据源-> GET/POST 发送请求->接收返回结果->断言测试结果->生成测试报告(html报告)->网页报告 2.pyth ...

  10. .Net Core ABP应用如何在阿里云Linux Docker中开启Https

    .Net Core应用开启Https本身就有很多种方式:1.代码配置2.环境变量3.反向代理 这里主要记录下阿里云的ECS,加阿里云免费的SSL证书,通过程序代码,如何进行配置. 首先从阿里云下载证书 ...