Python中的闭包使用及作用

 更新时间:2022年08月05日 15:35:12   作者:Python热爱者  
这篇文章主要介绍了Python中的闭包使用及作用,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下

1.什么是闭包

当我们在外部函数中定义了一个内部函数,并且内部函数能够读取到外部函数内的变量,这种函数我们就称为闭包。简单来说,闭包就是能够读取外部函数内的变量的函数。

闭包的架子大概是这样:

def demo_outer(x):
    def demo_inner(y):
        print("x的值:{}, y的值:{}, x + y 的值:{}".format(x, y, x + y))
    return demo_inner
do = demo_outer(12)
do(34)

上面代码执行结果如下:

x的值:12, y的值:34, x + y 的值:46

上面的闭包代码,和我们之前学习的装饰器类似,我们在外部函数 demo_outer 下定义了一个内部函数 demo_inner ,并且外部函数的返回值就是内部函数,同时在内部函数中,我们引用到了外部函数的变量 x ,而闭包的作用就是可以将外层函数的变量保存在内存中而不被销毁。

2.闭包的实例

我们先准备一个函数add(),每次调用该函数都只能传一个数 num ,每次返回的结果都是基于上一次结果的值 sum 进行累加操作。例如,sum的默认值为0,如果我们依次调用 add(10)、add(20)、add(30) 后,期望得到的最终结果是 sum = 60。

对于该问题,因为需要在函数内部对函数外部的变量进行处理,我们可能会考虑使用 global 来处理。

sum = 0
def get_add_sum(num):
    global sum
    sum += num
    return sum
print(get_add_sum(10))  # 输出:10
print(get_add_sum(20))  # 输出:30
print(get_add_sum(30))  # 输出:60
print(sum)  # 输出:60

上面代码中,我们在函数中通过全局变量 global 将 sum 声明为全局变量,最终返回的结果也符合我们的期望。但因为全局变量太灵活了,不同模块函数都能自由访问到全局变量,所以一般不推荐在函数内部中定义全局变量。

对于上面的问题,除了使用全局变量外,我们还可以通过 闭包 来实现。

sum = 0
def get_add_sum(sum):
    def add_num(num):
        nonlocal sum
        sum += num
        return sum
    return add_num

add = get_add_sum(sum)
print(add(10))  # 输出:10
print(add(20))  # 输出:30
print(add(30))  # 输出:60

print(sum)  # 输出:0

在上面的闭包函数中,定义了外层函数和内层嵌套函数,执行过程中,调用外层函数 get_add_sum 返回的就是内层嵌套函数 add_num ,因为Python中函数也是对象,所以可以直接返回 add_num 。

而在内层嵌套函数中则实现了我们想要的累加操作,在这里我们需使用关键字 nonlocal 来声明外层函数中的变量,否则无法对外层函数中的变量进行修改,其作用域只在闭包函数里面,所以当我们在最后打印 sum 最终结果,输出的仍然是其初始值 0 。

我们再来看一个例子:

def outer():
    res = []
    for i in range(3):
        print("外部的i值:{}".format(i))
        def inner(x):
            print("内部的i值:{}".format(i))
            return i + x
        res.append(inner)
    return res

temp = outer()
res = [i(10) for i in temp]
print(res)

上面代码的执行结果如下:

外部的i值:0
外部的i值:1
外部的i值:2
内部的i值:2
内部的i值:2
内部的i值:2
[12, 12, 12]

可以看到 res 的结果并不是 [10, 11, 12] ,因为 i 是外层函数的变量,并且其值是按 0,1,2 变化,因为该函数中无法保存外层函数的变量,故对于内部函数,其实际只能访问到最后外部变量的值 2 ,导致最终结果为:[12, 12, 12]。

在这里,我们可以使用闭包来保存函数的外部变量,修改代码如下:

def outer(i):
    print("外部的i值:{}".format(i))
    def inner(x):
        print("内部的i值:{}".format(i))
        return i + x
    return inner

def demo():
    res = []
    for i in range(3):
        res.append(outer(i))
    return res

temp = demo()
res = [i(10) for i in temp]
print(res)

上面代码的执行结果如下:

外部的i值:0
外部的i值:1
外部的i值:2
内部的i值:0
内部的i值:1
内部的i值:2
[10, 11, 12]

3.闭包和装饰器的区别

在Python中,闭包传递的参数是变量,装饰器传递的参数是函数对象,它们只是在传参内容上有不同。那么装饰器是不是属于闭包的一种呢,我们要怎么判断一个函数是否是闭包呢?

我们可以打印 闭包 和 装饰器 的属性 __closure__ ,如果一个函数是闭包,那么查看该属性将会返回一个cell对象组成的tuple对象。

def demo_outer(x):
    """
    闭包
    """
    def demo_inner(y):
        print("x的值:{}, y的值:{}, x + y 的值:{}".format(x, y, x + y))
    return demo_inner
def demo_decorator(func):
    """
    装饰器
    """
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@demo_decorator
def method():
    pass

do = demo_outer(5)  # 闭包
print("闭包的属性:{}".format(do.__closure__))
dd = demo_decorator(method)  # 装饰器
print("装饰器的属性:{}".format(dd.__closure__))

执行结果如下:

闭包的属性:(<cell at 0x0000023F48C3C8E8: int object at 0x00007FF92017D4A0>,)
装饰器的属性:(<cell at 0x0000023F48C3C8B8: function object at 0x0000023F48CB97B8>,)

所以结合上面的结果,我们也可以这样理解:装饰器本质上就是一个闭包函数,它只是一个传递函数对象的闭包函数。

到此这篇关于Python中的闭包使用及作用的文章就介绍到这了,更多相关Python闭包内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python中assert函数的使用(含源代码)

    Python中assert函数的使用(含源代码)

    本文主要介绍了Python中assert函数的使用(含源代码),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • Python+OpenCV实现边缘检测与角点检测详解

    Python+OpenCV实现边缘检测与角点检测详解

    这篇文章主要为大家详细介绍了如何通过Python+OpenCV实现边缘检测与角点检测,文中的示例代码讲解详细,对我们学习Python与OpenCV有一定的帮助,需要的可以参考一下
    2023-02-02
  • 解决Python httpx 运行过程中无限阻塞的问题

    解决Python httpx 运行过程中无限阻塞的问题

    这篇文章主要介绍了解决Python httpx 运行过程中无限阻塞的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • Python操作SQLite数据库的方法详解【导入,创建,游标,增删改查等】

    Python操作SQLite数据库的方法详解【导入,创建,游标,增删改查等】

    这篇文章主要介绍了Python操作SQLite数据库的方法,简单说明了sqlite数据库的相关概念,并结合实例形式较为详细的分析了Python针对sqlite数据库的导入,创建,游标,增删改查等操作技巧,需要的朋友可以参考下
    2017-07-07
  • Python流程控制语句的深入讲解

    Python流程控制语句的深入讲解

    这篇文章主要给大家介绍了关于Python流程控制语句的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Python具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2020-06-06
  • 关于最大池化层和平均池化层图解

    关于最大池化层和平均池化层图解

    这篇文章主要介绍了关于最大池化层和平均池化层图解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • 6个Python办公黑科技,助你提升工作效率

    6个Python办公黑科技,助你提升工作效率

    这篇文章主要介绍了Python办公黑科技,文中有非常详细的代码示例,对正在学习python的小伙伴们有非常好的帮助,需要的朋友可以参考下
    2021-08-08
  • Python画图小案例之多啦A梦叮当猫超详细注释

    Python画图小案例之多啦A梦叮当猫超详细注释

    在看了很多Python教程之后,觉得是时候做点什么小项目来练练手了,于是想来想去,用python写了一个童年记忆的卡通人物哆啦A梦,代码注释无比详细清楚,快来看看吧
    2021-09-09
  • python os.path模块使用方法介绍

    python os.path模块使用方法介绍

    os.path 模块是系统路径操作模块,但实际的原理可以把它认为是处理包含斜杠("/")和反斜杠("\")字符串的模块,其中,斜杠("/")是 linux 系统下的路径分隔符,和反斜杠("\")是 windows 系统下的路径分隔符
    2022-08-08
  • python 批量将中文名转换为拼音

    python 批量将中文名转换为拼音

    这篇文章主要介绍了python 批量将中文名转换为拼音,帮助大家更好的理解和使用python,感兴趣的朋友可以了解下
    2021-02-02

最新评论