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函数时打印日志的功能。

相关推荐
第二只羽毛几秒前
重载和继承的实践
java·开发语言
AndrewHZ4 分钟前
【图像处理基石】GIS图像处理入门:4个核心算法与Python实现(附完整代码)
图像处理·python·算法·计算机视觉·gis·cv·地理信息系统
光军oi36 分钟前
全栈开发杂谈————JAVA微服务全套技术栈详解
java·开发语言·微服务
帮帮志36 分钟前
目录【系列文章目录】-(关于帮帮志,关于作者)
java·开发语言·python·链表·交互
qiuiuiu4131 小时前
正点原子RK3568学习日记-GIT
linux·c语言·开发语言·单片机
草莓熊Lotso1 小时前
《C++ STL list 完全指南:从基础操作到特性对比,解锁链表容器高效用法》
开发语言·c++·list
二王一个今2 小时前
Python打包成exe(windows)或者app(mac)
开发语言·python·macos
一勺菠萝丶2 小时前
Mac 上用 Homebrew 安装 JDK 8(适配 zsh 终端)完整教程
java·python·macos
C嘎嘎嵌入式开发7 小时前
(2)100天python从入门到拿捏
开发语言·python
Stanford_11067 小时前
如何利用Python进行数据分析与可视化的具体操作指南
开发语言·c++·python·微信小程序·微信公众平台·twitter·微信开放平台