1. 什么是闭包:

  闭包 是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变量和方法传递到外部。 

 

2. 形成闭包的条件:

  1. 该函数体的整体类型为高阶函数(解释:一个函数可以作为参数传给另外一个函数,或者一个函数的返回值为另外一个函数(若返回值为该函数本身,则为递归),满足其一则为高阶函数)

  2. 内部函数引用了自由变量(该变量可以是外部函数的形参, 也可以是外部函数的局部变量)

  通俗来说就是定义在某一个函数内部的函数

3. 实现

  1. 下面来举一个简单的闭包(不修改变量outer_var,只做读取)例子:

def outer():
outer_var = 1
def inner():
inner_var = outer_var + 100
print('inner:\t%s' % inner_var)
return inner

  执行结果如下:  

  

  但是从变量的生存周期来看,该怎么理解呢?我们的变量outer_var是函数outer的一个本地变量,这意味着只有当函数outer正在运行的时候才会存在。根据我们已知的python运行模式,我们没法在函数outer返回之后继续调用函数inner,在函数inner被调用的时候,变量outer_var早已不复存在,可能会发生一个运行时错误。然而返回的函数inner居然能够正常工作。Python支持一个叫做函数闭包的特性,用人话来讲就是,嵌套定义在非全局作用域里面的函数能够记住它在被定义的时候它所处的封闭命名空间。这能够通过查看函数的func_closure属性得出结论,这个属性里面包含封闭作用域里面的值(只会包含被捕捉到的值,比如outer_var,如果在outer里面还定义了其他的值,封闭作用域里面是不会有的)记住,每次函数outer被调用的时候,函数inner都会被重新定义。现在变量outer_var的值不会变化,所以每次返回的函数inner会是同样的逻辑

  2. 通过子函数修改父函数的自由变量

  在实现该功能之前不得不提及两个关键字, global及nonlocal

# global
GLOABL_BASE = 100

def increaseNum1():
GLOABL_BASE = 99
print(GLOABL_BASE, id(GLOABL_BASE)) def increaseNum2():
global GLOABL_BASE
GLOABL_BASE = 99
print(GLOABL_BASE, id(GLOABL_BASE)) if __name__ == '__main__':
print("*"*30)
increaseNum1()
print(GLOABL_BASE, id(GLOABL_BASE))
print("*" * 30)
increaseNum2()
print(GLOABL_BASE, id(GLOABL_BASE))

可以发现, 函数体内部的GLOBAL_BASE为该函数内部的局部变量, 模块级别下的GLOBAL_BASE与该函数作用域不产生共享,如果将函数内的变量声明为模块级别, 那么就实现了修改全局变量的功能

  那么在闭包环境下,如果修改外部函数中的变量呢?

def outer(init_num):
print("outer:\t{0}".format(init_num))
def inner(step):
nonlocal init_num
init_num += step
print("inner:\t{0}".format(init_num))
return inner if __name__ == '__main__':
func = outer(0)
for step in range(3):
func(2)
print("*"*30)
for step in range(3):
outer(0)(2)
print("*" * 30)

解释: 修改变量的作用域级别实现了, 但为什么运行期间保留了父函数中的变量值,这正是闭包,父函数会等待子函数调用彻底结束时才会释放空间,这里的func对象的生命周期直到最后一次被执行(函数对象同样具备__call__方法)才释放。

3. 剖析相关概念

  1. 作用域:我们知道对于一个模块来说, 其可能包含函数、类等抽象结构, 宏观上来看, 一个模块划分了多个作用域, 且是层层包含的关系, 那么在一个函数内部的命名空间内当出现一个变量的引用时,他会优先在自己的命名空间内查找该变量,这里我们就以全局作用域以及局部作用域为例,来做一下验证

# -*- coding:utf-8 -*-
"""测试作用域"""
GLO = 100
def local_func():
LOC = 1
print(locals()) def main():
for k, v in globals().items():
print(k,v)
print("*"*30)
local_func() if __name__ == '__main__':
main()

  2.

  

4. 闭包的内存模型

浅谈python闭包及装饰器的更多相关文章

  1. python 闭包和装饰器

    python 闭包和装饰器 一.闭包闭包:外部函数FunOut()里面包含一个内部函数FunIn(),并且外部函数返回内部函数的对象FunIn,内部函数存在对外部函数的变量的引用.那么这个内部函数Fu ...

  2. python闭包与装饰器

    转自小马哥: 闭包和装饰器充分体现了Python语法糖的优雅感觉. 在本文中,我们的实验要完成两个工作,一个是加法,一个是累计调用加法的次数,最普通的Python程序可以这么写: def valida ...

  3. 高逼格利器之Python闭包与装饰器

    生活在魔都的小明,终于攒够了首付,在魔都郊区买了一套房子:有一天,小明踩了狗屎,中了一注彩票,得到了20w,小明很是欢喜,于是想干脆用这20万来装修房子吧(decoration): 整个装修过程,小明 ...

  4. Python—闭包和装饰器

    闭包 定义:内部函数对外部函数变量的引用,则将该函数与用到的变量称为闭包. 闭包必须满足以下三个条件: 必须有一个内嵌函数. 内嵌函数必须引用外部函数中的变量. 外部函数返回值必须是内嵌函数的引用. ...

  5. Python 简明教程 --- 22,Python 闭包与装饰器

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 当你选择了一种语言,意味着你还选择了一组技术.一个社区. 目录 本节我们来介绍闭包与装饰器. 闭包与 ...

  6. Python闭包及装饰器

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

  7. python闭包以及装饰器

    通俗的定义:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure).它只不过是个“内层”的函数,由一个名字(变量)来指代,而这个名字(变 ...

  8. 浅显易懂的谈一谈python中的装饰器!!

    hello大家好~~我是稀里糊涂林老冷,一天天稀里糊涂的. 前一段时间学习了装饰器,觉着这东西好高大上哇靠!!哈哈,一定要总结一下,方便以后自己查阅,也希望帮助其他伙伴们共同进步! 装饰器: 大家可以 ...

  9. python闭包和装饰器

    本文目录: 1. 闭包的解析和用法 2. 函数式装饰器 3. 类装饰器 一.闭包 闭包是一种函数,从形式上来说是函数内部定义(嵌套)函数,实现函数的扩展.在开发过程中,考虑到兼容性和耦合度问题,如果想 ...

随机推荐

  1. flutter 动画 practice

    import 'package:flutter/material.dart'; import 'dart:io'; import 'dart:async'; main() => runApp(M ...

  2. asBroadcastStream

    StreamSubscription sc = StreamSubscription(); Stream s = Stream(); sc.addStream(s); var bs = sc.stre ...

  3. JML规格编程系列——OO Unit3分析和总结

    本文是BUAA OO课程Unit3在课程讲授.三次作业完成.自测和互测时发现的问题,以及倾听别人的思路分享所引起个人的一些思考的总结性博客.主要包含JML相关梳理.SMT Solver验证.JML单元 ...

  4. 微信小程序 image组件坑

    远程图片 在真机上测试时 image组件只能显示http请求的图片, 对https 与 //xxx.xxx.xx 之类的不能显示. 可显示 'http://img.alicdn.com/i2/8323 ...

  5. weui中的picker使用js进行动态绑定数据

    解决方案; picker和Select组件是通过input标签绑定,可以先通过input的父级元素移除input标签,重新插入input标签,最后重新初始化picker或Select组件. <d ...

  6. js实现frame框架部分页面的刷新

    一.先来看一个简单的例子: 下面以三个页面分别命名为frame.html.top.html.bottom.html为例来具体说明如何做. frame.html 由上(top.html)下(bottom ...

  7. Linux expect实现自动登录

    expect expect可以让我们实现自动登录远程机器,并且可以实现自动远程执行命令.当然若是使用不带密码的密钥验证同样可以实现自动登录和自动远程执行命令.但当不能使用密钥验证的时候,我们就没有办法 ...

  8. linux下安装dotnet core

    windows下安装linux系统需要用到VMware 这个软件,可自行百度下载,然后安装centos7系统安装 centos下安装dotnetcore 在终端输入命令: sudo yum insta ...

  9. 阿里云服务器远程连接错误:由于一个协议错误(代码:0x112f),远程会话将被中断。

    2019年10月,阿里云服务器远程连接忽然无法登录.当时正在清理c盘空间,C盘只剩下30+M,忽然远程桌面掉线,以为断网了,再次远程桌面连接时,就出现一下错误. 解决方案:万能的重启!!!具体错误原因 ...

  10. layui 多个文件上传控件 整合缩减代码

    // 图片上传 upload.render({ elem: '.upload-img' // 点击上传的按钮统一使用该类 ,url: "{:url('image/upload')}" ...