我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Python Closure

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Python Closure

在计算机科学中,闭包 又称 词法闭包 或 函数闭包,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。闭包被广泛应用于函数式语言中。

从上面这段话中可以看出闭包的两个重要条件是引用自由变量函数,与闭包这个名称结合起来看,这个函数就像是一个包,而这个函数所引用的变量就好比函数这个包中封闭起来的东西,包中的东西被紧紧封闭在包中,函数所引用的变量也被与这个函数所绑定。

首先来看两个概念 Nonlocal variable 和 Nested function

Nonlocal variable是相对于某个函数来说的,指的是这个函数所调用的在本函数作用域之外的变量,Nested function指的被定义在一个函数(outer enclosing function)中的函数,这个nested function可以调用包围它的作用域中的变量。

看一个例子

def print_msg(msg):
    # outer enclosing function

    def printer():
        # nested function
        print(msg)

    printer()

>>> print_msg("Hello")
Hello

在这个例子中函数printer就是一个nested function,而变量msg就是一个nonlocal variable。这里需要注意的是,printer虽然可以访问msg,但是不可以改变它,如果尝试更改会出现UnboundLocalError: local variable 'msg' referenced before assignment

def print_msg(msg):
    def printer():
        msg += 'a'
        print(msg)
    printer()

>>> print_msg("Hello")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in print_msg
  File "<stdin>", line 3, in printer
UnboundLocalError: local variable 'msg' referenced before assignment
local variable 'msg' referenced before assignment

如果必须要更改这个变量的值,在Python3中新引入的nonlocal语句可以解决。

def print_msg(msg):
    def printer():
        nonlocal msg
        msg += 'a'
        print(msg)
    printer()

>>> print_msg("Hello")
Helloa

在Python2中使用global也可解决,但是global会直接查找全局变量,而nonlocal则是按优先级从本地-->全局进行搜索。

下面使外层函数(outer enclosing function)返回一个函数

def print_msg(msg):
    def printer():
        print(msg)
    return printer

>>> another = print_msg("Hello")
>>> another()
Hello

print_msg("Hello")返回的函数赋值给another,再调用another函数时,发现已经离开了print_msg函数的作用域,但是"Hello"已经被绑定给another,所以仍然能够正常调用,这就是Python中的闭包。

删除print_msg之后,another仍然能够正常调用。

>>> del print_msg
>>> print_msg("Hello")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'print_msg' is not defined
name 'print_msg' is not defined

>>> another()
Hello

当符合下面几个条件时就形成了闭包:

  • 有一个Nested function

  • 这个Nested function访问了父函数作用域中的变量

  • 父函数返回了这个Nested function

闭包主要运用在需要讲父函数作用域中的变量绑定到子函数的场景之中,在释放掉父函数之后子函数也不会受到影响。运用闭包可以避免对全局变量的使用。对于一个只有需要实现少数方法的类我们也可以用闭包来替代,这样做可以减少资源的使用。

下面需要用类定义不同动物的叫声

class Animal:
    def __init__(self, animal):
        self.animal = animal
    def sing(self, voice):
        return "{} sings {}".format(self.animal, voice)

>>> dog = Animal("dog")
>>> cow = Animal("cow")
>>> dog.sing("wong")
'dog sings wong'
>>> cow.sing("mow")
cow sings mow'

用闭包替代

def make_sing(animal):
    def make_voice(voice):
        return "{} sings {}".format(animal, voice)
    return make_voice

>>> dog = make_sing("dog")
>>> dog("wong")
'dog sings wong'
>>> cow = make_sing("cow")
>>> cow("mow")
'cow sings mow'

闭包通常用来实现一个通用的功能,Python中的装饰器就是对闭包的一种应用,只不过装饰器中父函数的参数是一个函数,下面这个例子通过装饰器实现了在子函数执行前后输出提示信息。

def make_wrap(func):
    def wrapper(*args):
        print("before function")
        func(*args)
        print("after function")
    return wrapper

@make_wrap
def print_msg(msg):
    print(msg)

>>> print_msg("Hello")
before function
Hello
after function

装饰器也可以进行叠加

def make_another(func):
    def wrapper(*args):
        print("another begin")
        func(*args)
        print("another end")
    return wrapper

@make_another
@make_wrap
def print_msg(msg):
    print(msg)

>>> print_msg("Hello")
another begin
before function
Hello
after function
another end

Code Object

为了了解闭包的内部实现,需要用compile命令得出相应的code object

>>> code_obj = compile("print_msg('Hello')", "", "single")

这里第一个参数是一个可以被execeval解析的模块、语句或者表达式;

第二个参数是用来存放运行时错误的文件;

第三个选择single模式,与前面第一个参数填写的表达式相匹配,如果第一个参数是表达式则需要用eval模式,如果是模块则应该用exec模式。

下面通过discode_obj反编译成助记符

>>> dis.dis(code_obj)
  1           0 LOAD_NAME                0 (print_msg)
              2 LOAD_CONST               0 ('Hello')
              4 CALL_FUNCTION            1
              6 PRINT_EXPR
              8 LOAD_CONST               1 (None)
             10 RETURN_VALUE

Python3中通过__code__访问函数的code object(Python2中为func_code)

>>> print_msg.__code__
<code object print_msg at 0x10d5c7300, file "<stdin>", line 1>

Cell Object

cell object用来存储被多个作用域所引用的变量。

比如下面函数中msgprint_msg所引用,也被printer所引用,所以msg会被存在一个cell object

def print_msg(msg):
    def printer():
        print(msg)
    return printer

查看其__closure__属性可以验证我们的想法

>>> print_msg("Hello").__closure__
(<cell at 0x10d121d38: str object at 0x10d4a6f48>,)

尽管这两个引用都被存在同意个cell object,但是他们仍然只在各自的作用域下作用。

闭包分析

首先反编译print_msg

>>> dis.dis(print_msg)
  2           0 LOAD_CLOSURE             0 (msg)
              2 BUILD_TUPLE              1
              4 LOAD_CONST               1 (<code object printer at 0x10d5c7780, file "<stdin>", line 2>)
              6 LOAD_CONST               2 ('print_msg.<locals>.printer')
              8 MAKE_FUNCTION            8
             10 STORE_FAST               1 (printer)

  4          12 LOAD_FAST                1 (printer)
             14 RETURN_VALUE
  • LOAD_CLOSURE 0 (msg)将变量msg进栈。

  • BUILD_TUPLE 1 将栈顶的元素取出,创建元组,并将该元组push进栈。

  • LOAD_CONST 1print_msg.__code__.co_consts[1]中取出,为printercode object的地址,将其push进栈。

  • LOAD_CONST 2print_msg.__code__.co_consts[2]中取出,将其push进栈。

  • STORE_FAST 1从栈顶取出之前创建的函数对象的地址信息赋给局部变量printer(局部变量名记录在__code__.co_varnames中)
    __code__.co_varnames的内容为('msg','printer')

将变量msg(记录在__code__.co_cellvars[0])绑定栈顶的函数对象地址。

  • LOAD_FAST 1msg的值压入栈。

  • RETURN_VALUE返回栈顶。

可以看到在STORE_FAST 1中将变量msg绑定到了printer函数,从而达到了闭包中内部函数访问外部函数变量的效果。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Python Closure

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Python Closure

在计算机科学中,闭包 又称 词法闭包 或 函数闭包,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。闭包被广泛应用于函数式语言中。从上面这段话中可以看出闭包的两个重要条件是引用自由变量和
2023-01-31

如何理解Python中的闭包Closure

如何理解Python中的闭包Closure,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Python中的闭包不是一个一说就能明白的概念,但是随着你往学习的深入,无论如何你都
2023-06-17

PHP中怎么使用Closure类

小编给大家分享一下PHP中怎么使用Closure类,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Closure 类用于代表匿名函数的类。匿名函数(在 PHP 5.
2023-06-07

JavaScript闭包closure有什么作用

本篇内容主要讲解“JavaScript闭包closure有什么作用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JavaScript闭包closure有什么作用”吧!一、什么是闭包闭包(clos
2023-06-21

PHP7中如何使用Closure :: call

这篇“PHP7中如何使用Closure :: call”文章,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要参考一下,对于“PHP7中如何使用Closure :: call”,小编整理了以下知识点,请大家跟着小编的步
2023-06-14

javascript中闭包closure的代码案例

这篇文章主要讲解了“javascript中闭包closure的代码案例”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“javascript中闭包closure的代码案例”吧!简介闭包closu
2023-06-06

JavaScript中的闭包closure怎么使用

这篇文章主要介绍“JavaScript中的闭包closure怎么使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“JavaScript中的闭包closure怎么使用”文章能帮助大家解决问题。闭包简述
2023-07-02

MySQL数据库闭包Closure Table表实现示例

目录1、 数据库闭包表简介2、创建节点表3、创建关系表4、创建存储过程添加数据5、插入测试数据6、查询 华东 下所有的子节点7、查询 华东 下直属子节点8、查询 六合区 所处的层级9、闭包表的优缺点和适用场景1、 数据库闭包表简介像mys
2023-01-13

[Python] python-docx

[目的]使用Python编辑Micorsoft Word 文档[测试环境]        OS: MS XP professional SP3 Win32        Python:2.6.6[准备]        python-docx
2023-01-31

Python: kafka-python

背景我们有个数据处理平台,有两个用 docker 运行的数据处理模块,分别是:data_api, 和 processor_api,故名思义:data_api: 接受数据;processor_api: 处理数据;数据处理简单架构踩坑
2023-01-31

[Python]linux python

#!/usr/bin/env python与#!/usr/bin/python的区别脚本语言的第一行,目的就是指出,你想要你的这个文件中的代码用什么可执行程序去运行它,就这么简单 #!/usr/bin/python是告诉操作系统执行这个脚本
2023-01-31

python-初识python

一.计算机的初步认识   硬件:内存,主板,CPU,硬盘,显卡,显示器等  内存:    优点:存取速度快    缺点:断电后数据会全部丢失  硬盘:    优点:断电后数据也会永久保存    缺点:存取速度远远慢于内存1.操作系统  wi
2023-01-31

python------用python写

这样一个小游戏,我们用shell也是可以完成的,但是这里我们主要是练习python!具体的要求及shell的写法http://zidingyi.blog.51cto.com/10735263/1767566!#!/usr/bin/env p
2023-01-31

Python之——python-nmap

转载请注明出处:http://blog.csdn.net/l1028386804/article/details/78995578一、python-nmap安装# yum -y install nmap #安装nmap工具# 模块pip
2023-01-31

【python】IDLE与python(

这个是idle:这个是Python (command line):idle是GUI形式,command line是命令提示符形式,都是和Python解释器交互的手段。Module Docs模块手册意思是Python各种模块的说明书
2023-01-31

python python-docx 替

最根本的参考当然还是官方的文档,相当不错的中文讲解。 有人已经做了如何修改一些格式的博客:用python-docx修改已存在的Word文档的表格的字体格式。 我这里给出替换内容给的例子,简单来说,就是使用一个函数.clear()清除原来内容
2023-01-31

Python 2.7 和 Python

Python 2 发布于 2000 年年底,意味着较之先前版本,这是一种更加清晰和更具包容性的语言开发过程。而先前版本的 Python 应用的是 PEP (Python 增强协议),这种技术规范能向 Python 社区成员提供信息或描述这种
2023-01-31

【Python】Python-pool的

收集了几个python种进程池pool的使用例子,改写为py3版本。1. pool.apply_async进程非阻塞执行,输入不确定情况下用默认情况下,Pool会创建固定数目的工作进程,并向这些工作进程传递作业,直到再没有更多作业为止。当有
2023-01-31

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录