python闭包理解

目录

Python中对闭包相关定义

(一)、闭包的定义

(三)、闭包的作用

二、python中闭包的设计底层机制保障

(一)、nonlocal关键字

(二)、函数如存在被引用,则函数不释放

(三)、嵌套函数机制

三、闭包的设计典型代码

四、闭包功能设计分析

(一)、数据封装:

(二)、存在类设计做数据封装,为什么需要闭包功能?

(三)、类的变量封装

1、应用场景:

2、实现机制:

3、功能特点:

(四)、闭包的数据封装

1、应用场景:

2、实现机制:

3、功能特点:

4、两者对比

5、保持变量状态

6、延迟计算

7、为什么要设计闭包的延迟计算

五、闭包技术必要性和产生原因及优势

(一)、产生原因

(二)、闭包设计优势

1、结构性紧凑

2、逻辑顺序严谨

3、调用方式不变

(三)、闭包带来问

-------------------------------------------------------------------------------------------------------------------------

一、Python中对闭包相关定义

(一)、闭包的定义

在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,就把这个使用了外部函数变量的内部函数称为闭包

(二)、闭包构成条件

1、存在函数嵌套
2、内部函数使用了外部函数的变量(包括外部函数的参数)
3、外部函数返回了内部函数

(三)、闭包的作用

1、数据封装:闭包可以用于封装私有数据,只暴露有限的接口供外界访问。 

2、保持变量状态:闭包允许函数记住和访问其词法作用域中的变量,即使函数在其作用域之外执行。

3、延迟计算:通过闭包可以推迟计算的执行,直到真正需要结果时。

二、python中闭包的设计底层机制保障
(一)、nonlocal关键字

通过nonlocal关键字,内层函数可以访问外层函数变量。    

为了让内存函数能引用外层函数中的变量,python在底层机制上设计加了nonlocal关键字,如果没有这个关键字,内层函数就无法引用外部变量,闭包大部分设计初衷(数据封装等)就无法完成。

(二)、函数如存在被引用,则函数不释放

B函数能访问A函数:但B函数被包裹在A里面,在闭包这种设计机制下,如果最终的B函数还是不能被外界调用,没有意义。于是这时候我把B函数引出来,通过A函数返回。那么这时就B函数就相当于暴露给外界了,随便调用了,也就变成正常函数了。

(三)、嵌套函数机制

函数内部定义函数的机制
比如C语言就没有原生支持嵌套函数。python闭包通过嵌套函数,巧妙的把内部接口引出。

Python底层通过支持上述机制,从而使得闭包的设计形式称为可能。


三、闭包的设计典型代码

通过上述代码设计,外层counter函数通过接口可以访问内部变量,由于Python设计成返回后函数不销毁的机制,那么里面的变量可以长期存在,而外部函数又能通过接口访问它,也让这个变量有事实上全局变量的功能。

四、闭包功能设计分析

(一)、数据封装:

上面代码就是闭包提供的典型数据封装例子,函数内局部变量可以替代全局变量。具体过程如下图:

(二)、存在类设计做数据封装,为什么需要闭包功能?

那如果闭包只是为了提供这种形式的局部变量,在Python中,类的变量封装和闭包都是实现数据封装的手段,但它们的应用场景、实现机制以及提供的功能有所不同。

(三)、类的变量封装

1、应用场景:

类主要用于定义对象的蓝图,它封装了数据(属性)和操作这些数据的方法(行为)。当你需要创建多个具有相同结构但不同数据的对象时,类是一个自然的选择。

2、实现机制:

类通过将变量标记为私有(通常以两个下划线__开头)来限制外部直接访问,从而实现封装。私有变量只能通过类的公有方法(getter和setter)来访问和修改,这样可以控制对数据的访问,并在必要时进行验证或转换。

3、功能特点:

提供了面向对象编程的特性,如继承、多态等。

可以通过实例化创建多个独立的对象,每个对象有自己的状态。

更易于维护和扩展,特别是在大型项目中,类的封装有助于模块化设计。

(四)、闭包的数据封装

1、应用场景:

闭包常用于函数式编程,当需要创建具有特定状态的函数时,闭包可以捕获并记住其周围作用域的变量。适用于创建一次性使用的工具函数、装饰器或是管理状态的小型逻辑单元。

2、实现机制:

闭包是由一个内部函数引用其外部函数作用域中的变量而形成的。即使外部函数执行完毕,内部函数仍然可以访问这些外部变量。这种机制自然地“封装”了这些变量,使得它们不被外部直接访问。

3、功能特点:

适用于不需要或不希望创建对象的情况,减少内存占用。

提供了一种轻量级的状态管理方式,特别适合处理短暂存在的状态或计算过程。

便于编写简洁、可读性强的代码,尤其是在处理函数式编程任务时。

4、两者对比

类的封装更适合需要复杂对象结构、面向对象特性和长期状态管理的场景。

闭包的数据封装则更适合处理函数间的状态传递、短期状态保持或简单的数据隐藏需求,特别是在不需要完整类结构的情况下。

两者各有优势,选择哪种方式取决于具体的需求、项目的规模以及设计哲学。

5、保持变量状态

和闭包的数据封装基本相同。

6、延迟计算

延迟计算实际上典型应用在装饰器上,现在以装饰器为例说明。

有时候闭包并没有外部函数引用局部变量的需求,闭包定义中要求内部函数使用了外部函数的变量,其实这时候并没有引用外层函数中的外部变量,只是需要增加的功能直接放在外层函数中。 在闭包典型引用中的装饰器就是这样的。第一次只是传入原被装饰函数,返回内层函数地址后,将返回这个地址直接复制给原被装饰函数命名的地址变量。然后利用这个地址变量,带入被装饰函数的参数直接再次调用内层函数。

编辑

7、为什么要设计闭包的延迟计算

import time

def log_decorator(func):
    start_time = time.time()
    func()
    end_time = time.time()
    execution_time = end_time - start_time
    print(f"Function {func.__name__} executed in {execution_time} seconds")

def say_hello():
    time.sleep(1)
    print("Hello, world!")

log_decorator(say_hello)

上述代码也没有设计成闭包形式,但是同样达到效果了。为什么不采用这种方式?

我的理解有两点:

一是改变了调用方式:原来调用形式为:say_hello(),现在为log_decorator(say_hello)

二是上面形式如果装饰器本身有参数的话,,只能写成

def log_decorator(parm)(func)形式,内层外层函数没有分开,层次不分明,调用形式比较繁琐,不够简洁。

五、闭包技术必要性和产生原因及优势

(一)、产生原因

闭包技术产生需求本质是横切面编程的需要,横切面编程中需要一个函数访问(获得)另外一个函数内部值,或者需要给另外一个函数增加额外功能,于是横切面编程结合面向对象编程,所以就催生了闭包技术。

(二)、闭包设计优势

1、结构性紧凑

两个函数绑定在一起,你想访问我内部函数吗?那你必须被我包裹起来。代码逻辑上一看就知道谁引用了我。

2、逻辑顺序严谨

把B定义包含在A里面,第一次调用A,只是执行了A,这就保证了逻辑的先后顺序的100%可靠。第二次才执行B本身,然后再由A返回给第三方调用。

3、调用方式不变

访问了A函数内部变量的B函数,当然其他函数C要能引用B才有意义,正好B包机制能返回B函数地址供调用。保证了调用形式不变,设计非常巧妙。

(三)、闭包带来问题

正常设计机制应该外层函数执行完返回值后应该销毁,但是为了符合闭包设计初衷,外层函数有指向地址的函数返回后不释放,PYTHON加上这个机制。但是大量这种应用将会造成内存不释放,最终造成系统崩溃。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值