Python:返回函数到闭包
·
1. 高阶函数(Higher-Order Function)
高阶函数就是能把函数当作"东西"来使用的函数——要么接收函数作为参数,要么返回函数作为结果。
1.1 基础示例
# 示例1:接收函数作为参数
def do_math(a, b, math_function):
"""math_function是一个函数,比如加法或乘法函数"""
return math_function(a, b) # 使用传入的函数进行计算
def add(x, y):
return x + y
def multiply(x, y):
return x * y
# 使用过程拆解:
result1 = do_math(5, 3, add) # 把add函数传给do_math → do_math调用add(5,3) → 8
result2 = do_math(5, 3, multiply) # 把multiply函数传给do_math → do_math调用multiply(5,3) → 15
1.2 另一个例子:map函数(python内置高阶函数)
# map是高阶函数,接收函数和可迭代对象
numbers = [1, 2, 3, 4, 5]
def square(x):
return x * x
# 使用拆解:
squared_numbers = map(square, numbers) # map接收square函数和numbers列表
# 相当于对每个元素调用square函数
# 结果:[1, 4, 9, 16, 25]
1.3 更直观的理解
把函数想象成不同的计算方式:
# 定义不同的计算方式
def add_numbers(a, b):
return a + b
def subtract_numbers(a, b):
return a - b
# 高阶函数:接收计算方式作为参数
def calculate(operation, x, y):
"""operation是一个函数,告诉calculate如何计算"""
return operation(x, y)
# 使用拆解:
sum_result = calculate(add_numbers, 10, 5) # 用加法计算:10+5=15
difference_result = calculate(subtract_numbers, 10, 5) # 用减法计算:10-5=5
2. 返回函数(Function as Return Value)
返回函数不直接给你结果,而是给其内部的另一个函数。
2.1 基础示例
def get_calculation_function(operation_type):
"""根据操作类型返回相应的计算函数"""
# 定义几个简单的计算函数
def add(x, y):
return x + y
def multiply(x, y):
return x * y
# 返回函数本身(不是调用结果)
if operation_type == "add":
return add
else:
return multiply
# 使用过程拆解:
add_function = get_calculation_function("add") # 给予operation_type参数"add"返回内部加法函数
multiply_function = get_calculation_function("multiply") # 返回内部乘法函数
# 现在使用获得的函数:
result1 = add_function(5, 3) # 使用内部加法函数add(x,y):5+3=8
result2 = multiply_function(5, 3) # 使用内部乘法函数multiply(x,y):5×3=15
2.2 更简单的例子:选择问候方式
def choose_greeting(time_of_day):
"""根据时间选择问候函数"""
def morning_greet(name):
return f"早上好,{name}!"
def evening_greet(name):
return f"晚上好,{name}!"
if time_of_day == "morning":
return morning_greet
else:
return evening_greet
# 使用拆解:
morning_greeter = choose_greeting("morning") # 返回早上问候函数
evening_greeter = choose_greeting("evening") # 返回晚上问候函数
greeting1 = morning_greeter("小明") # 使用早上问候函数
greeting2 = evening_greeter("小红") # 使用晚上问候函数
3. 闭包(Closure)
闭包就是有"记忆"的函数:它不仅能完成工作,还能记住自己所在环境的条件(参数)。
3.1 基础示例
def create_personal_calculator(base_number):
"""创建一个带基数的个性化计算器"""
def calculator(x):
return base_number + x # 这个函数记住了base_number
return calculator # 返回有记忆的计算器
# 使用过程完整拆解:
add_five = create_personal_calculator(5) # 步骤1:创建计算器,让它记住5
# → create_personal_calculator(5)执行
# → base_number = 5
# → 定义calculator函数(此时calculator记住了base_number=5)
# → 返回calculator函数给add_five
# → add_five(x) 等价于 calculator(x), 即return 5+x
result = add_five(3) # 步骤2:使用计算器
# → 调用calculator(3)
# → calculator使用记住的base_number=5
# → 计算:5 + 3 = 8
3.2 闭包的"记忆"特性
def create_smart_counter():
"""创建一个能记住自己数到哪里的计数器"""
current_count = 0 # 计数器记住的初始状态
def count():
nonlocal current_count # 允许修改记住的状态
current_count += 1
return current_count
return count # 返回有记忆的计数函数
# 使用拆解:
counter1 = create_smart_counter() # 创建计数器1,初始记忆:0
counter2 = create_smart_counter() # 创建计数器2,初始记忆:0
print(counter1()) # 计数器1:0→1,返回1
print(counter1()) # 计数器1:1→2,返回2(记得上次是1)
print(counter2()) # 计数器2:0→1,返回1(独立的记忆)
print(counter1()) # 计数器1:2→3,返回3(继续自己的记忆)
- 初始值
count=0在执行一次后被覆盖 - 状态记忆在闭包环境中,每次调用都访问和修改同一个变量,存在调用的闭包环境中
- 每个闭包实例有独立的状态,互不干扰
nonlocal让内部函数可以修改外部函数的变量,实现状态保持
4. nonlocal在闭包中的作用
nonlocal就像是对函数说:“这个变量不是你的,是你外面那个函数的,你可以调用它。”
4.1 问题场景
def create_broken_counter():
count = 0 # 外部函数的变量
def increment():
# 这里会出错!Python认为count是increment自己的变量
count = count + 1 # 🚫 错误:右边的count还没有定义
return count
return increment
# 错误原因拆解:
# Python看到"count = ...",认为count是increment的局部变量
# 但执行"count + 1"时,右边的count还没有值,所以报错
4.2 nonlocal解决方案
def create_working_counter():
count = 0 # 外部函数的变量
def increment():
nonlocal count # 声明:count不是我的,是外面那个函数的
count = count + 1 # ✅ 现在可以修改外部函数的count了
return count
return increment
# 使用拆解:
my_counter = create_working_counter() # 创建计数器,count=0
print(my_counter()) # 步骤1:nonlocal指向外部count=0 → 0+1=1 → 返回1
print(my_counter()) # 步骤2:nonlocal指向外部count=1 → 1+1=2 → 返回2
print(my_counter()) # 步骤3:nonlocal指向外部count=2 → 2+1=3 → 返回3
4.3 nonlocal的简单比喻
# 就像父子关系:
def father():
family_money = 100 # 爸爸的钱
def son():
nonlocal family_money # 声明:我要用爸爸的钱,不是自己的
family_money -= 20 # 使用爸爸的钱
return f"花了20元,家里还剩{family_money}元"
return son
# 使用拆解:
spend_money = father() # 爸爸给儿子权限:初始化了family_money=100的函数son()
print(spend_money()) # 执行spend_money()就是执行了有family_money=100的函数son();儿子花爸爸的钱:100-20=80
print(spend_money()) # 儿子再花:80-20=60
5. 四者关系:从简单到复杂
5.1 关系演进
1. 高阶函数:函数可以作为参数或返回值(基础)
↓
2. 返回函数:函数返回另一个函数(应用)
↓
3. 闭包:返回的函数记住创建环境(进阶)
↓
4. nonlocal:让闭包可以修改记住的环境(完善)
5.2 完整学习路径示例
# 第1步:高阶函数(基础)
def use_function(func, value):
return func(value) # 使用传入的函数
# 第2步:返回函数(应用)
def get_operation(operation_name):
def add(x, y):
return x + y
def multiply(x, y):
return x * y
if operation_name == "add":
return add
else:
return multiply
# 第3步:闭包(进阶)- 函数有记忆
def create_calculator_with_memory(base):
def calculate(x):
return base + x # 记住base的值
return calculate
# 第4步:nonlocal(完善)- 可以修改记忆
def create_updatable_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
6. 注意:闭包与循环变量的陷阱
6.1 问题示例
def create_buttons():
functions = []
for i in range(3): # i是循环变量
def button_click():
return f"按钮 {i} 被点击了"
functions.append(button_click)
return functions
# 创建三个按钮函数
f1,f2,f3 = create_buttons()
print(f1()) # 期望:"按钮 0 被点击了"
print(f2()) # 期望:"按钮 1 被点击了"
print(f3()) # 期望:"按钮 2 被点击了"
实际输出结果:
按钮 2 被点击了
按钮 2 被点击了
按钮 2 被点击了
6.2 问题分析
为什么会出现这个问题?
- 循环执行时:创建函数就开始了循环,使
i从 0 → 1 → 2 - 闭包创建时:不论如何循环,三个
button_click函数都引用同一个变量i,即functions=[f(i),f(i),f(i)],因为每次返回的都是button_click()函数,和i的循环值无关 - 函数调用时:循环已经结束,
i的最终值是 2 - 结果:所有函数都使用最终的
i=2
内存状态变化:
循环开始:i = 0 → 创建函数1(引用i)
循环继续:i = 1 → 创建函数2(引用i)
循环继续:i = 2 → 创建函数3(引用i)
循环结束:i = 2(最终值)
调用函数1:使用当前i的值(2)
调用函数2:使用当前i的值(2)
调用函数3:使用当前i的值(2)
6.3 解决方案:使用默认参数绑定
def create_buttons_simple():
functions = []
for i in range(3):
def button_click(index=i): # 使用默认参数绑定当前i值
return f"按钮 {index} 被点击了"
functions.append(button_click)
return functions
# 使用拆解:
f1,f2,f3 = create_buttons_simple()
print(f1()) # 输出:"按钮 0 被点击了"
print(f2()) # 输出:"按钮 1 被点击了"
print(f3()) # 输出:"按钮 2 被点击了"
为什么这个方案有效?
- 默认参数在函数定义时求值,而不是调用时
index=i在循环的每一步都会将当前的i值绑定为默认参数- 每个函数都有自己独立的默认参数值
7. 总结
- 高阶函数:把函数当"工具"传递和使用
- 返回函数:返回其内部的另一个函数,其内部的函数才是最终执行任务的角色
- 闭包:有记忆的函数,记住创建时的环境
- 闭包与循环变量的陷阱: 闭包处于循环当中时,要将循环值绑定到内部函数的默认参数中
- nonlocal:让内部函数可以修改外部函数的变量
更多推荐


所有评论(0)