Python 闭包

Python 闭包

大家接触过python肯定都知道闭包这个东西,但是有些人看过也就忘记了,感觉对他的用处也不是很明朗,只是知道有这个东西,那么今天就随着我一起学习这个闭包吧!

闭包介绍

简单来说: 内部函数对外部函数作用域内变量的引用(非全局变量),则称内部函数为闭包。乍一听好像也是丈二和尚摸不着头脑,那么就让我们看代码来进一步了解吧!

代码介绍

1
2
3
4
5
6
7
8
def outer():
message = "welcome to python"
def inner():
print(f"{message}")
inner()

outer()

那么各位同学可以猜想一下,运行结束后,输出什么呢? 我想各位都猜对了,那就是 welcome to python, 其实这个就是一个闭包, 我们的inner是内部程序,这时候我们用到了外部的outer的变量也就是message,这样我们就成为一个闭包。应该比较容易理解吧!那么下一步我会进行一个程序变形,那么你能看出下一步输出什么吗?

1
2
3
4
5
6
7
8
9
def outer():
message = "welcome to python"
def inner():
print(f"{message}")
message = "wow python is amazing!"
return inner

f = outer()
f()

那么我们现在改完后,各位同学还能猜出输出什么吗?哈哈,可能部分同学认为输出的依旧是“welcome to python”,理由可能如下:

我们运行outer的时候实际上返回了inner函数,此时inner执行的时候直接就会输出 outer里面的message,所以输出的是 welcome to python。 那么事实真的如此吗?我们来运行看看!

可惜,可能和部分同学想的不一样了,那么为什么会输出wow python is amazing 呢?下面我来讲解一下:

闭包的原理

其实,我们在执行上述程序的时候,先运行的outer,此时实际上python悄默默的的做了一件事情,那就是将Message存了起来,等到inner运行的时候从里面拿出来,那么同学说我怎么看不到呢?下面使用debug模式我们来看一下!

大家可以看到我debug的时候查看一个内部函数,f.__closure__ 这个是什么呢? 其实这个就是闭包函数,closure 翻译成闭包就是中文的意思,那么此时做了什么事情呢? 实际上 __closure__ 是一个元组类型的,实际上是进行了一个复制操作将wow python is amazing 复制了一份放在了元祖中,所以在使用的时候实际上是从这个地方去出来的数据,为了证实我们可以进一步来看!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def outer():
message = "welcome to python"
name = "xiao ming "

def inner():
print(f"{name}+{message}")

# message = "wow python is amazing!"
return inner


f = outer()
f()

运行结果:

我们可以看出确实将信息存储在了__closure__ 中,如果你理解了上述的,你也就明白了闭包为什么是内部函数使用外部函数的变量信息。

使用场景

说了这么多,那么闭包的应用场景有哪些呢? 那就一起来看吧!

1. 数据封装和隐私保护

闭包可以用来封装数据,使得数据只通过特定的函数接口进行访问和修改,从而保护数据的隐私性。

1
2
3
4
5
6
7
8
def outer(text):
def inner():
print(text)
return inner

# 使用闭包
my_printer = outer("Hello, World!")
my_printer() # 输出: Hello, World!

在这个例子中,text 变量被封装在 outer 函数的作用域中,只有通过 inner 函数才能访问。

2. 装饰器

Python 中的装饰器是一种常用的闭包应用场景。装饰器允许你在不修改原有函数代码的情况下,给函数增加新的功能。

关于装饰器可以看这我这篇更加详细的介绍:https://8888666.top/2024/08/07/python%E8%A3%85%E9%A5%B0%E5%99%A8/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper

@my_decorator
def say_hello():
print("Hello!")

say_hello()
# 输出:
# Something is happening before the function is called.
# Hello!
# Something is happening after the function is called.

3. 工厂函数

工厂函数是返回函数的函数,这些返回的函数可以捕获并操作创建它们时的环境。这常用于生成具有特定行为或状态的函数实例。

1
2
3
4
5
6
7
8
9
10
def make_multiplier_of(n):
def multiplier(x):
return x * n
return multiplier

times_two = make_multiplier_of(2)
times_three = make_multiplier_of(3)

print(times_two(10)) # 输出: 20
print(times_three(10)) # 输出: 30

4. 回调函数

在某些异步编程或事件驱动编程场景中,回调函数经常被用作闭包。它们允许在将来某个时间点调用一个函数,同时保持对当前环境或数据的引用。

1
2
3
4
5
6
7
8
9
10
def perform_action(action, value):
result = action(value)
print(f"The result is {result}")

def add_ten(x):
return x + 10

perform_action(add_ten, 5) # 闭包的概念在这里不是直接体现,但回调函数常与闭包结合使用

# 假设在异步或事件驱动环境中,回调函数作为闭包传递,以保持对外部环境的引用

5. 缓存机制

闭包可以用来实现缓存机制,尤其是当函数计算代价较高且其结果可以重用时。通过缓存函数的返回值,可以显著提高程序的性能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def memoize(func):
cache = {}
def inner(*args):
if args in cache:
return cache[args]
result = func(*args)
cache[args] = result
return result
return inner

@memoize
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)

# 调用 fibonacci 多次,可以看到缓存的效果

这些只是闭包在 Python 中应用的几个例子,实际上闭包的应用场景非常广泛,几乎可以在任何需要封装数据或函数逻辑的场景中使用。


Python 闭包
https://dreamshao.github.io/2024/08/05/python闭包/
作者
Yun Shao
发布于
2024年8月5日
许可协议