python函数——闭包

概念介绍:

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

闭包就是内层函数,对外层函数(非全局)的变量的引用,叫闭包 可以让一个局部变量常驻内存,防止其他程序修改这个变量

初始闭包

为了说明闭包中引用的变量的性质,可以看一下下面的这个例子:

python 复制代码
def f2(func):
    def f1():
        x = func()
        return x + 1
    return f1
def func():
    print("输出func()函数")
    return 1
dec = f2(func)
print(dec())
print(func())

输出结果为:

还可以将 f1( ) 函数的定义移动到 f2( )函数中,这样在 f2( ) 函数外的作用 域就不能直接调用 f1( ) 函数

闭包的概念:

如果内层函数引用了外层函数的变量(包括其参数),并且外层函数 返回内层函数名,这种函数架构称为闭包。

闭包满足的如下3个条件:

  1. 内层函数的定义嵌套在外层函数中。

  2. 内层函数引用外层函数的变量

  3. 外层函数返回内层函数名

闭包陷阱:

python 复制代码
def my_func(*args):
    fs = []
    for i in range(3):
        def func():
            return i * i
        fs.append(func)
    return fs

fs1, fs2, fs3 = my_func()
print(fs1())
print(fs2())
print(fs3())

上面这段代码可谓是典型的错误使用闭包的例子。程序的结果并不是我们想象的结果0,1,4。实际结果全部是4。

这个例子中,my_func返回的并不是一个闭包函数,而是一个包含三个闭包函数的一个list。这个例子中比较特殊的地方就是返回的所有闭包函数均引用父函数中定义的同一个自由变量。

但这里的问题是为什么for循环中的变量变化会影响到所有的闭包函数?尤其是我们上面刚刚介绍的例子中明明说明了同一闭包的不同实例中引用的自由变量互相没有影响的。而且这个观点也绝对的正确。

那么问题到底出在哪里?应该怎样正确的分析这个错误的根源。

其实问题的关键就在于在返回闭包列表list之前for循环的变量的值已经发生改变了,而且这个改变会影响到所有引用它的内部定义的函数。因为在函数my_func返回前其内部定义的函数并不是闭包函数,只是一个内部定义的函数。所以要避免这个情况。

可以通过下面这种方式进行0、1、4的输出,从而使得这个返回s的外部函数不冲突

python 复制代码
def my_func(*args):
    fs = []
    for i in range(3):
        def func(i = i):
            return i * i
        fs.append(func)
    return fs
fs1, fs2, fs3 = my_func()
print(fs1())
print(fs2())
print(fs3())

闭包的应用:

闭包(closure)是指在一个函数内部定义的函数,并且这个内部函数可以访问到其外部函数的局部变量。Python中闭包的应用场景很多,主要可以用于以下几个方面:

保持状态信息:闭包可以用来保持某些信息的状态,使得函数每次调用时可以记住上次调用的结果或状态。这在某些需要记忆或记录状态的场景中非常有用。

python 复制代码
def counter():
    count = 0
    def inner():
        nonlocal count
        count += 1
        return count
    return inner

c = counter()
print(c())  # 输出 1
print(c())  # 输出 2
print(c())  # 输出 3

在这个例子中,内部函数inner形成了闭包,可以访问并修改外部函数counter的局部变量count,从而实现了计数的功能。

封装私有变量:通过闭包,可以创建私有变量或函数,这些变量和函数对外部是不可见的,从而实现类似于面向对象编程中私有成员的效果。

python 复制代码
def private_counter():
    count = 0
    def increment():
        nonlocal count
        count += 1
        return count
    def decrement():
        nonlocal count
        count -= 1
        return count
    return increment, decrement

inc, dec = private_counter()
print(inc())  # 输出 1
print(inc())  # 输出 2
print(dec())  # 输出 1

在这个例子中,incrementdecrement函数形成了闭包,可以访问并修改private_counter函数的局部变量count,但这个count对外部是不可见的。

实现装饰器:装饰器本质上就是闭包,它可以在不修改原函数代码的情况下,给函数添加额外的功能

python 复制代码
def logger(func):
    def wrapper(*args, **kwargs):
        print(f'Calling function {func.__name__} with args {args} and kwargs {kwargs}')
        return func(*args, **kwargs)
    return wrapper

@logger
def add(a, b):
    return a + b

print(add(3, 5))  # 输出 Calling function add with args (3, 5) and kwargs {},以及 8

这里的logger函数是一个闭包,它接受一个函数作为参数,返回一个新的函数wrapper,实现了在调用add函数时打印日志的功能。

相关推荐
FinAnalyzer2 分钟前
如何在 InsCodeAI 上搭建并使用 Jupyter Notebook 环境?
ide·python·jupyter
java1234_小锋4 分钟前
【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 微博文章数据可视化分析-文章分类下拉框实现
python·自然语言处理·flask
檀越剑指大厂5 分钟前
【Python系列】Flask 应用中的主动垃圾回收
开发语言·python·flask
檀越剑指大厂11 分钟前
【Python系列】使用 memory_profiler 诊断 Flask 应用内存问题
开发语言·python·flask
笠码13 分钟前
JVM Java虚拟机
java·开发语言·jvm·垃圾回收
WXX_s23 分钟前
【OpenCV篇】OpenCV——03day.图像预处理(2)
人工智能·python·opencv·学习·计算机视觉
橙小花28 分钟前
C语言:指针、变量指针与指针变量、数组指针与指针数组
c语言·开发语言
Cyanto1 小时前
MyBatis-Plus高效开发实战
java·开发语言·数据库
艾莉丝努力练剑1 小时前
【LeetCode&数据结构】二叉树的应用(二)——二叉树的前序遍历问题、二叉树的中序遍历问题、二叉树的后序遍历问题详解
c语言·开发语言·数据结构·学习·算法·leetcode·链表
Jackilina_Stone2 小时前
【论文|复现】YOLOFuse:面向多模态目标检测的双流融合框架
人工智能·python·目标检测·计算机视觉·融合