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

相关推荐
m0_7485548135 分钟前
golang如何实现用户订阅偏好管理_golang用户订阅偏好管理实现总结
jvm·数据库·python
smj2302_796826521 小时前
解决leetcode第3911题.移除子数组元素后第k小偶数
数据结构·python·算法·leetcode
阿正呀2 小时前
Redis怎样实现本地缓存的高效失效通知
jvm·数据库·python
九转成圣2 小时前
Java 性能优化实战:如何将海量扁平数据高效转化为类目字典树?
java·开发语言·json
SmartRadio2 小时前
ESP32-S3 双模式切换实现:兼顾手机_路由器连接与WiFi长距离通信
开发语言·网络·智能手机·esp32·长距离wifi
2501_901200532 小时前
mysql如何设置InnoDB引擎参数_优化innodb_buffer_pool
jvm·数据库·python
laowangpython2 小时前
Rust 入门:GitHub 热门内存安全编程语言
开发语言·其他·rust·github
我叫汪枫2 小时前
在后台管理系统中,如何递归和选择保留的思路来过滤菜单
开发语言·javascript·node.js·ecmascript
_.Switch2 小时前
东方财富股票数据JS逆向:secids字段和AES加密实战
开发语言·前端·javascript·网络·爬虫·python·ecmascript
软件技术NINI2 小时前
webkit简介及工作流程
开发语言·前端·javascript·udp·ecmascript·webkit·yarn