2.1 python装饰器基础:从语法糖到高阶函数
2.1 装饰器基础:从语法糖到高阶函数
- 2.1 装饰器基础:从语法糖到高阶函数
- 2.1 装饰器基础:从语法糖到高阶函数
- 高阶函数:装饰器的基石
- 装饰器语法糖:优雅的简写
- 深入理解装饰器执行流程
- 处理带参数的函数
- 保留原函数的元信息
- 装饰器的实际应用场景
2.1 装饰器基础:从语法糖到高阶函数
2.1 装饰器基础:从语法糖到高阶函数
装饰器是 Python 中最强大且优雅的特性之一,它允许开发者在不修改原函数代码的前提下,为其添加新功能。理解装饰器的关键在于掌握两个核心概念:高阶函数和语法糖。本节将深入剖析这两个概念,揭示装饰器的工作机制。
高阶函数:装饰器的基石
在 Python 中,函数是一等公民(First-class Citizen)。这意味着函数可以像其他数据类型(如整数、字符串)一样被传递、赋值和作为返回值。高阶函数(Higher-order Function)是指能够接受其他函数作为参数,或者将函数作为返回值的函数。这正是装饰器能力的源泉。
让我们通过一个简单的例子来理解高阶函数:
def greet(name):return f"Hello, {name}!"def shout(func):"""一个高阶函数,它接受一个函数并返回一个新函数"""def wrapper(name):# 在调用原函数的基础上添加新行为:将结果转为大写original_result = func(name)new_result = original_result.upper() + "!!!"return new_resultreturn wrapper# 使用高阶函数:将 greet 函数传递给 shout
enhanced_greet = shout(greet)
print(enhanced_greet("Alice")) # 输出:HELLO, ALICE!!!!
在这个例子中,shout 是一个高阶函数:
- 它接受一个函数
func作为参数。 - 它内部定义了一个新函数
wrapper。 - 它最终返回这个新函数
wrapper。
当我们调用 enhanced_greet = shout(greet) 时,实际上创建了一个增强版的 greet 函数。这个新函数在保留原 greet 功能的同时,还添加了将结果转为大写并添加感叹号的行为。
比喻理解:想象一个咖啡店。你点了一杯普通咖啡(原函数 greet)。咖啡师(高阶函数 shout)可以在这杯咖啡的基础上添加糖、牛奶或肉桂粉(新功能),然后给你一杯调制好的特色咖啡(增强版函数 enhanced_greet),而不需要改变咖啡本身的制作方法。
装饰器语法糖:优雅的简写
虽然上面的高阶函数用法完全有效,但 Python 提供了一种更加优雅的写法——装饰器语法糖(Decorator Syntax Sugar),使用 @ 符号。
让我们用装饰器语法重写上面的例子:
def shout(func):def wrapper(name):original_result = func(name)new_result = original_result.upper() + "!!!"return new_resultreturn wrapper@shout
def greet(name):return f"Hello, {name}!"# 现在直接调用 greet 就已经是增强版的了
print(greet("Alice")) # 输出:HELLO, ALICE!!!!
@shout 这行代码实际上等价于我们之前写的 greet = shout(greet)。这种语法更加直观和简洁,将装饰逻辑与函数定义放在一起,提高了代码的可读性。
深入理解装饰器执行流程
为了更清晰地理解装饰器的工作机制,让我们添加一些打印语句来跟踪执行流程:
def my_decorator(func):print("步骤1:装饰器函数被调用,接收原函数")def wrapper():print("步骤3:wrapper 函数在执行原函数前添加新功能")result = func() # 调用原函数print("步骤4:wrapper 函数在执行原函数后添加新功能")return resultprint("步骤2:装饰器返回 wrapper 函数")return wrapper@my_decorator
def say_hello():print("步骤5:原函数执行核心逻辑")return "Hello World!"print("准备调用被装饰的函数...")
print(say_hello())
运行这段代码,你会看到如下输出:
步骤1:装饰器函数被调用,接收原函数
步骤2:装饰器返回 wrapper 函数
准备调用被装饰的函数...
步骤3:wrapper 函数在执行原函数前添加新功能
步骤5:原函数执行核心逻辑
步骤4:wrapper 函数在执行原函数后添加新功能
Hello World!
这个执行流程揭示了几个关键点:
- 装饰时机:装饰器在函数定义时立即执行,而不是在函数调用时。
- 函数替换:原函数
say_hello被替换成了wrapper函数。 - 调用链:当我们调用
say_hello()时,实际上是在调用wrapper()。
处理带参数的函数
在实际应用中,我们经常需要装饰带参数的函数。这需要我们在 wrapper 函数中使用 *args 和 **kwargs 来接收任意数量的参数:
def universal_decorator(func):def wrapper(*args, **kwargs):print(f"即将调用函数: {func.__name__}")print(f"参数: args={args}, kwargs={kwargs}")# 将参数原样传递给原函数result = func(*args, **kwargs)print(f"函数返回: {result}")return resultreturn wrapper@universal_decorator
def add(a, b):return a + b@universal_decorator
def greet(name, greeting="Hello"):return f"{greeting}, {name}!"print(add(3, 5))
print(greet("Bob", greeting="Hi"))
这种通用的参数处理方式使得装饰器可以应用于几乎任何函数,无论其参数签名如何。
保留原函数的元信息
当使用装饰器时,原函数会被 wrapper 函数替换,这会导致原函数的一些元信息(如函数名、文档字符串等)丢失:
@shout
def greet(name):"""一个简单的问候函数"""return f"Hello, {name}!"print(greet.__name__) # 输出:wrapper
print(greet.__doc__) # 输出:None
为了解决这个问题,Python 提供了 functools.wraps 装饰器,它能够将原函数的元信息复制到包装函数中:
import functoolsdef shout(func):@functools.wraps(func) # 保留原函数的元信息def wrapper(*args, **kwargs):original_result = func(*args, **kwargs)new_result = original_result.upper() + "!!!"return new_resultreturn wrapper@shout
def greet(name):"""一个简单的问候函数"""return f"Hello, {name}!"print(greet.__name__) # 输出:greet
print(greet.__doc__) # 输出:一个简单的问候函数
最佳实践:在定义装饰器时,总是使用 @functools.wraps(func) 来装饰内部的 wrapper 函数,这是一个被广泛遵循的约定。
装饰器的实际应用场景
装饰器在实际开发中有着广泛的应用:
- 日志记录:自动记录函数的调用信息和执行时间
- 权限验证:在函数执行前检查用户权限
- 性能监控:测量函数执行时间
- 缓存:存储函数计算结果,避免重复计算
- 输入验证:自动验证函数参数的合法性
import time
import functoolsdef timer(func):"""测量函数执行时间的装饰器"""@functools.wraps(func)def wrapper(*args, **kwargs):start_time = time.perf_counter()result = func(*args, **kwargs)end_time = time.perf_counter()print(f"函数 {func.__name__} 执行耗时: {end_time - start_time:.4f} 秒")return resultreturn wrapper@timer
def expensive_operation():"""模拟一个耗时操作"""time.sleep(1)return "操作完成"print(expensive_operation())
通过这个基础章节的学习,我们已经建立了装饰器的核心概念:装饰器本质上是高阶函数的语法糖,它通过接收一个函数、包装它、然后返回包装后的函数来工作。这种模式为后续学习更复杂的装饰器用法(如带参数的装饰器、类装饰器等)奠定了坚实的基础。
