如果一个函数内部定义了另一个函数,并且内部函数引用了外部函数的变量,那么内部函数就形成了一个闭包。
def outer_function(x):
# 外部函数接受一个参数 x 是自由变量
# seed 也是一个自由变量
seed = 10
def inner_function(y):
# 内部函数接受另一个参数 y
return x + y + seed
return inner_function
# 创建闭包函数,传入参数 10,closure就是一个闭包
closure = outer_function(10)
# 使用闭包函数计算 5 + 10 + 10
result = closure(5)
print(result) # 输出 25
https://docs.python.org/zh-cn/3.7/reference/executionmodel.html#index-6
局部变量,如果名称绑定在一个代码块中,则为该代码块的局部变量。
全局变量,如果名称绑定在模块层级,则为全局变量。
自由变量,如果变量在一个代码块中被使用但不是在其中定义,则为 自由变量。
1、 闭包可以**捕获(即使外部函数已经执行完毕,这些变量依然可以被内部函数访问和操作)外部变量,**并且保持外部变量的状态,使其在多次调用中保持不变。
2、闭包允许函数返回一个函数,而不仅仅是一个值。
3、闭包与闭包之间的状态是隔离的
def average():
data = [] # 使用列表来存储内部状态
def add_number(number):
data.append(number) # 将新数字添加到列表中
total = sum(data) # 计算列表中所有数字的总和
count = len(data) # 获取列表中数字的数量
return total / count if count > 0 else 0 # 计算平均数
return add_number
# 创建累计平均数的闭包
avg = average()
# 不断添加新的数字并计算平均数
# data变量是average函数的局部变量
# 但是 当调用avg(10)时,average函数已经执行完了,所以它的作用域已经不存在了
print(avg(10)) # 平均数: 10.0
print(avg(20)) # 平均数: 15.0
print(avg(30)) # 平均数: 20.0
print("avg --> ", avg(40)) # 平均数: 25.0
# 闭包和闭包之间的状态是隔离的
avg1 = average()
print("avg1 --> ", avg1(11))
print("avg1 --> ", avg1(15))
本地作用域在函数结束后就立即失效,而嵌套作用域在嵌套的函数返回后却仍然有效,类似可以把这些变量类比为 C++中局部静态变量。想要给函数增加或者保持状态、实现装饰器、构建工厂函数、创建函数组合就可以使用闭包来实现。
##################### 函数组合
def add(x):
return x + 2
def multiply(x):
return x * 3
def compose(f, g):
# 返回一个闭包,将 f(g(x)) 的结果
def inner(x):
return f(g(x))
return inner
# 创建函数组合
combined_function = compose(add, multiply)
# 使用组合函数
result = combined_function(4) # 先执行 multiply(4),然后执行 add(12)
print(result) # 输出 14
################### 创建工厂函数
def create_multiplier(factor):
# 工厂函数返回一个闭包
def multiplier(x):
return x * factor
return multiplier
# 创建两个不同的乘法函数工厂
double = create_multiplier(2)
triple = create_multiplier(3)
# 使用工厂函数生成乘法函数
double_result = double(5) # 返回 5 * 2 = 10
triple_result = triple(5) # 返回 5 * 3 = 15
print(double_result)
print(triple_result)
################# 装饰器
import time
def timing_decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
execution_time = end_time - start_time
print(f"{func.__name__} executed in {execution_time:.4f} seconds")
return result
return wrapper
# 使用装饰器
@timing_decorator
def requests_http_data():
# 模拟一些耗时操作
time.sleep(2)
requests_http_data()
global 声明对全局变量进行引用修改
nonlocal 内嵌函数内部想对嵌套作用域中的值是不可变类型的变量(值为 int、float、str)进行修改
n = 100
def add():
global n # 函数内部要对全局变量进行修改,必须使用global声明
n = n +100
print(n)
add()
print(n)
def sub():
a = 100
def execs():
nonlocal a # 内嵌的函数想修改外部函数的变量,必须使用nonlocal进行声明
a = a - 1
return a
return execs
s = sub()
print(s())
闭包比较像只有一个方法的类,可以保持状态和数据隐藏,为什么不写成类:
1、闭包的功能一般很小很简单
2、闭包执行速度较快,不需要多余的self参数等
# 闭包
def counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
# 创建闭包对象
counter1 = counter()
counter2 = counter()
print(counter1()) # 输出 1
print(counter1()) # 输出 2
print(counter2()) # 输出 1
print(counter1()) # 输出 3
# 类
class Counter:
def __init__(self):
self.count = 0
def increment(self):
self.count += 1
return self.count
# 创建类对象
counter1 = Counter()
counter2 = Counter()
print(counter1.increment()) # 输出 1
print(counter1.increment()) # 输出 2
print(counter2.increment()) # 输出 1
print(counter1.increment()) # 输出 3
# 偏函数,也可以保持函数内部的变量状态
# 我们可以使用内置的 functools 模块的 partial 函数来创建偏函数。
# 偏函数指通过固定函数的一部分参数后,返回一个新的函数,
# 这个新函数可以接受剩余的参数进行调用
from functools import partial
def add(a, b):
return a + b
x = partial(add, 1) # 1赋给参数a 并暂停函数
print(x)
res1 = x(2) # 将2赋给b后进行计算
print(res1) # 3
res2 = x(3)
print(res2) # 4