视频链接:
https://space.bilibili.com/70431433?spm_id_from=333.1007.0.0
文章代码链接:
https://github.com/zyf-ngu/Qmatter
4高阶函数
内部函数-闭包
在 Python 中,内部函数(嵌套函数)可以直接访问外部函数的参数和变量,这种特性称为闭包(Closure)。闭包允许内部函数捕获并记住外部函数的环境,即使外部函数已经执行完毕。
闭包类似 “实例化” 的效果 —— 创建多个独立的 “函数实例”,它们拥有各自独立的状态,互不干扰。通过 “局部状态记忆” 替代全局变量,将状态封装在函数内部,避免全局变量易引发命名冲突(如多个模块定义count变量)。
闭包的典型应用(记住外部环境)
def make_counter():
count = 0 # 外部函数的变量
def counter():
nonlocal count
count += 1
return count
# 返回内部函数(闭包)
return counter
# 创建计数器实例
counter1 = make_counter()
counter2 = make_counter()
# 调用闭包,每个实例会记住自己的count状态
print(counter1()) # 1
print(counter1()) # 2
print(counter2()) # 1
print(counter1()) # 3
print(counter2()) # 2
关键规则:
- 读取外部参数 / 变量:内部函数可以直接读取外部函数的参数和变量。
- 修改限制:默认情况下,内部函数不能直接修改外部函数的不可变类型变量(如 int、str、tuple),但可以修改可变类型变量(如 list、dict)。
强制修改:如果需要修改外部函数的不可变变量,可以使用nonlocal关键字声明。
以下是关于 Python 嵌套函数(闭包)的具体实例,展示了不同情况下内部函数对外部变量的访问和修改方式:
实例 1:内部函数读取外部变量
def outer_function(message):
# 外部函数的变量
outer_var = "外部变量"
def inner_function():
# 内部函数读取外部变量和参数
print(f"内部函数访问外部参数: {message}")
print(f"内部函数访问外部变量: {outer_var}")
# 调用内部函数,一般用于多线程处理
inner_function()
# 调用外部函数
outer_function("外部参数")
输出:
内部函数访问外部参数: 外部参数
内部函数访问外部变量: 外部变量
实例 2:修改外部可变类型变量(list/dict)
def outer_function():
# 外部函数的可变类型变量(列表)
outer_list = [1, 2, 3]
def inner_function():
# 直接修改外部可变类型变量(无需特殊声明)
outer_list.append(4)
print(f"内部函数修改后的列表: {outer_list}")
inner_function()
return outer_list
# 调用外部函数
result = outer_function()
print(f"外部函数返回的列表: {result}")
输出:
内部函数修改后的列表: [1, 2, 3, 4]
外部函数返回的列表: [1, 2, 3, 4]
实例 3:修改外部不可变类型变量(需用 nonlocal)
def outer_function():
# 外部函数的不可变类型变量(整数)
outer_num = 10
def inner_function():
#声明需要修改外部不可变变量
nonlocal outer_num
outer_num = 20 # 现在可以修改了
print(f"内部函数修改后的数值: {outer_num}")
inner_function()
return outer_num
# 调用外部函数
result = outer_function()
print(f"外部函数返回的数值: {result}")
输出:
内部函数修改后的数值: 20
外部函数返回的数值: 20
装饰器函数
我们知道,函数是封装了特定功能的代码块,前面也介绍了函数参数的具体传递,不过参数均是简单的变量。如果我们想对函数的功能进行扩展应该怎么做呢?直接修改函数代码必然不是一个好选择,python提供了装饰器。
装饰器是Python中一个非常强大的功能,它允许我们修改或增强函数、方法或类的行为,而不需要改变其本身的代码。装饰器本质上是一个可调用对象(通常是一个函数),它接受一个函数作为参数,并返回一个新的函数。新函数包括2部分,核心部分是原函数的功能,第2部分就是新增的模式化的通用功能。
其基本结构如下:
#1 定义一个接收函数为参数的装饰器函数
def decorator_function(func):
#2定义一个包装函数(wrapper),参数使用*args,**kwargs,以适应各种参数的原函数,这里参数和原函数是对应的
def wrapper(*args,**kwargs):
语句1#3.在原函数前后增加一些通用的功能语句,也可以使用上面传递进来的参数
result=func(*args,**kwargs)#4.调用原函数
语句2
return result#5原函数有返回值则返回值,包装函数结束
return wrapper#最后返回扩展函数,装饰器函数结束
# 6 使用方式,@+装饰器函数名,下面跟着接收函数的定义,之后正常调用接收函数
@decorator_function
def func:
pass
举个例子,在调用原函数前后添加了额外日志记录的功能。
def log_decorator(func):
defwrapper(*args, **kwargs):
for num in args:#内部扩展函数的其它语句也可以使用
print('num:',num)
print(f"Before function {func.__name__} is called.")
result = func(*args, **kwargs)
print(f"After function {func.__name__} is called.")
return result
return wrapper
@log_decorator# 接收函数为参数的函数名前面加@符号即为装饰器。
def add(a, b):#原函数定义在下方
return a + b
result = add(3, 5)
print(result)
# 带参数的装饰器需要三层嵌套
def require_role(role):
#外层接收装饰器参数
def decorator(original_func):
# 中间层接收函数
def wrapper(*args, **kwargs):
# 内层实现装饰逻辑
if role == "admin":
print("管理员权限验证通过")
return original_func(*args, **kwargs)
else:
raise PermissionError("权限不足!")
return wrapper
return decorator
# 应用带参数的装饰器
@require_role("admin")
def delete_database():
print("数据库已删除!")
@require_role("user")
def view_data():
print("显示数据...")
# 测试
delete_database() # 成功执行
try:
view_data() # 触发异常
except PermissionError as e:
print(f"错误: {e}")
总结一下,装饰器函数本质上是一个函数,其主要功能和作用体现在以下几个方面:
1. 代码复用和模块化
装饰器可以将一些通用的功能(如日志记录、权限验证、性能测试等)封装起来,然后应用到多个不同的函数上,避免了代码的重复编写,提高了代码的复用性和可维护性。
2. 功能增强
在不修改原函数代码的前提下,为原函数添加额外的功能。这符合软件开发中的开闭原则,即对扩展开放,对修改关闭。
3. 分离关注点
将与核心业务逻辑无关的功能(如日志、权限控制等)从业务逻辑中分离出来,使代码结构更加清晰,便于维护和管理。
注意事项-
装饰器会改变被装饰函数的元信息(如函数名、文档字符串等),可以使用`functools.wraps`来保留这些信息。
- 装饰器在模块导入时就会执行,因此装饰器内部的代码会在导入时运行。
使用functools.wraps保留原始函数的元信息,
import functools
def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("装饰器中...")
return func(*args, **kwargs)
return wrapper
@my_decoratordef example():
print("Hello")
print(example.__name__) # 输出 'example' 而不是 'wrapper'
print(example.__doc__) # 输出 '示例函数的文档字符串' 而不是 '包装函数的文档字符串'```
匿名函数(Lambda 函数)
匿名函数(Lambda 函数)是一种不需要使用def关键字定义的函数,它使用lambda关键字创建。Lambda 函数是一个小型、匿名的函数,可以接受任意数量的参数,但只能有一个表达式。
语法:
func(可选,函数对象)=lambdaarguments: expression
原理:
Lambda 函数在运行时创建,返回一个函数对象。这个函数对象可以像普通函数一样被调用,参数在调用时传递进去,但它没有函数名(因此称为"匿名"函数)。
简洁性:对于简单的操作,使用 lambda 函数比定义完整函数更简洁
一次性使用:适用于只需要使用一次的小型函数
函数式编程:常与高阶函数(如 map(), filter(), reduce())配合使用
回调函数:作为参数传递给其他函数
# 1. lambda基本用法
add= lambda x, y: x + y
print(add(5, 3)) # 输出: 8
# 2. 与普通函数对比
def add_def(x, y):
return x + y
# Lambda 版本更简洁
# 3. 在排序中使用
students = [("Alice", 90), ("Bob", 85), ("Charlie", 95)]# 按分数排序
sorted_students = sorted(students, key=lambda x: x[1])
print(sorted_students) # 输出: [('Bob', 85), ('Alice', 90), ('Charlie', 95)]
# 4. 立即调用 lambda 函数
result = (lambda x, y: x * y)(5, 6)
print(result) # 输出: 30
高阶函数
高阶函数是指能够接收函数作为参数或将函数作为返回值的函数。在 Python 中,函数是一等对象(first-class objects),这意味着函数可以像其他对象一样被传递和操作。通过将函数作为参数或返回值,实现了更高层次的抽象和代码复用。
抽象通用模式:将通用操作模式抽象出来,具体行为通过函数参数定制
代码复用:减少重复代码,提高代码的可维护性
函数组合:通过组合简单函数构建复杂功能
延迟执行:可以创建并返回函数,在需要时执行
高阶函数、闭包和装饰器之间的关系可以概括为:装饰器是闭包的一种应用,而闭包又是高阶函数的一种形式。
高阶函数是指能够接收函数作为参数,或者将函数作为返回值的函数。
闭包是指在一个内部函数中,对外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包。闭包允许函数记住并访问其词法作用域中的变量,即使函数是在其作用域之外执行。
装饰器本质上是一个函数,它接收一个函数作为参数,并返回一个新的函数。装饰器通常用于在不修改原函数代码的情况下,为函数添加新的功能。
装饰器是利用闭包和高阶函数的特性来实现的。
装饰器是一个高阶函数,因为它接收函数作为参数并返回函数。
装饰器内部通常会定义一个闭包(内部函数),这个闭包会引用外部函数的参数(即被装饰的函数)以及外部作用域的变量。
# 1. 接收函数作为参数的高阶函数(lambda函数作为参数)
def apply_operation(func, x, y):
"""应用给定的操作到两个参数上"""
return func(x, y)
# 使用不同的操作
result1 = apply_operation(lambda a, b: a + b, 5, 3)
result2 = apply_operation(lambda a, b: a * b, 5, 3)
print(result1, result2) # 输出: 8 15
#2. Python 内置高阶函数
from functools import reduce
numbers = [1, 2, 3, 4, 5]
# map() - 对每个元素应用函数
squared = list(map(lambda x: x**2, numbers))
print(squared) # 输出: [1, 4, 9, 16, 25]
# filter() - 过滤满足条件的元素
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # 输出: [2, 4]
# reduce() - 累积计算序列中的元素
sum_all = reduce(lambda x, y: x + y, numbers)
print(sum_all) # 输出: 15