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. JDBC中PreparedStatement相比Statement的好处

    Statement对象: 用于执行不带参数的简单SQL语句: 特点: a. 只执行单条的sql语句: b. 只能执行不带参数的sql语句: c.运行原理的角度,数据库接收到sql语句后需要对该条sql ...

  2. [LeetCode] 581. 最短无序连续子数组 ☆

    描述 给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序. 你找到的子数组应是最短的,请输出它的长度. 示例 1: 输入: [2, 6, 4, 8 ...

  3. 【Python】单例模式

    单例 class MusicPlayer(object): instance = None def __new__(cls, *args, **kwargs): if cls.instance is ...

  4. Java中重载(overloading)和重写(Overriding)的区别

    一:方法的重载 (1)方法重载指在类中定义方法名相同,参数不同的不同的多个方法(返回值类型可随意,不能以返回类型作为重载函数的区分标准). 参数不同表现: 1.参数的个数不同 2.参数的类型不同 3. ...

  5. unity 编译之后签名被改变

    原因:在gradle 模式下编译,把development build 勾上 会使签名改变

  6. centos 7.6 安装php70

    1.首先查看是否有老版本 yum list installed | grep php 2.如果安装的有,清除老版本 yum remove php.x86_64 php-cli.x86_64 php-c ...

  7. 采用MySQL-MMM做DB高可用时,遇到的一个小坑

    一.服务器分布   二.MySQL-MMM 配置 (1).公共配置[所有DB节点:Master1.Master2.Slave1.Slave2   Monitor节点] # vim /etc/mysql ...

  8. Vue过渡效果的实现

    1.Vue 过渡组件 Vue 在插入.更新或者移除 DOM 时,使用内置的过渡封装组件可以实现过渡效果 语法格式: <transition name = "xx"> & ...

  9. Microsoft Onenote shortcuts / Onenote快捷键大全

    Onenote跟Libreoffice ,有道笔记比起来一个快捷键特别不太好用,就是Ctrl + Shift +v 并不会提供一个选择粘贴模式选项. 而在Onenote 中 Ctrl + Shift ...

  10. Kotlin继承与重写重要特性剖析

    继续Kotlin的面向对象之旅. 继承: 在Java中我们知道除了final类不能被继承,其它的情况都是可以被继承的,而在Kotlin中的规则是这样的:“在Kotlin中,所有类在默认情况下都是无法被 ...