理解装饰器的基本原理:函数增强而不修改原代码
掌握带参数函数的装饰器写法(使用 *args 和 **kwargs)
了解函数作为“一等公民”和高阶函数在装饰器中的作用
熟悉函数装饰器、方法装饰器、类装饰器的使用场景
掌握 Python 内置装饰器 @staticmethod、@classmethod、@property
能够实现多装饰器链式调用并理解执行顺序
装饰器是 Python 中一种优雅的元编程工具,用于在不修改原始函数代码的前提下,动态地为其添加新功能(如日志、权限校验、缓存等)。从本质上讲,装饰器是一个接受函数作为参数并返回新函数的高阶函数。
def decorator(func):
def wrapper():
print("Before calling the function.")
func()
print("After calling the function.")
return wrapper
@decorator
def greet():
print("Hello, World!")
greet()输出:
Before calling the function.
Hello, World!
After calling the function.等价写法:greet = decorator(greet)@decorator 是语法糖,使代码更简洁清晰。
实际函数通常带有参数,因此装饰器的内部函数需使用 *args 和 **kwargs 以兼容所有函数签名。
def log_execution(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result}")
return result
return wrapper
@log_execution
def add(a, b):
return a + b
print(add(5, 3))输出:
Calling add with args=(5, 3), kwargs={}
add returned 8
8关键:
wrapper必须能接收任意位置参数和关键字参数,并将它们原样传递给原函数。
Python 中的函数具有以下特性,这是装饰器得以实现的基础:
可赋值给变量
可作为参数传递
可作为函数返回值
可存储在数据结构中
# 1. 赋值
def say_hello(name):
return f"Hello, {name}!"
greet = say_hello
# 2. 作为参数
def apply(func, value):
return func(value)
# 3. 作为返回值
def make_multiplier(n):
def multiply(x):
return x * n
return multiply
double = make_multiplier(2)
print(double(5)) # 10这些特性使得函数可以像普通对象一样被操作,为装饰器提供了可能。
高阶函数是指:
接受一个或多个函数作为输入,或
返回一个函数作为输出
装饰器正是典型的高阶函数:它接收一个函数,返回一个增强后的新函数。
def apply_twice(func):
def inner(x):
return func(func(x))
return inner
def increment(x):
return x + 1
double_increment = apply_twice(increment)
print(double_increment(3)) # 5 (3 → 4 → 5)用于增强普通函数的行为。
def timer(func):
import time
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start:.4f} seconds")
return result
return wrapper
@timer
def slow_function():
import time
time.sleep(0.1)
slow_function()用于类中的方法,需处理 self 参数。
def method_logger(func):
def wrapper(self, *args, **kwargs):
print(f"Calling method: {func.__name__}")
return func(self, *args, **kwargs)
return wrapper
class Calculator:
@method_logger
def add(self, a, b):
return a + b
calc = Calculator()
print(calc.add(2, 3))输出:
Calling method: add
5作用于整个类,可用于添加属性、方法或修改类行为。
def add_class_info(cls):
cls.class_name = cls.__name__
cls.module = cls.__module__
return cls
@add_class_info
class User:
pass
print(User.class_name) # User
print(User.module) # __main__@staticmethod定义不依赖实例或类状态的方法,无需 self 或 cls。
class MathUtils:
@staticmethod
def square(x):
return x * x
print(MathUtils.square(4)) # 16@classmethod定义操作类本身的方法,第一个参数是 cls(类对象)。
class Person:
species = "Homo sapiens"
@classmethod
def get_species(cls):
return cls.species
print(Person.get_species()) # Homo sapiens常用于替代构造函数(工厂方法):
class Date:
def __init__(self, year, month, day):
self.year, self.month, self.day = year, month, day
@classmethod
def from_string(cls, date_str):
y, m, d = map(int, date_str.split('-'))
return cls(y, m, d)
d = Date.from_string("2025-09-22")@property将方法变为只读或可控的属性,支持封装。
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value < 0:
raise ValueError("Radius cannot be negative")
self._radius = value
@property
def area(self):
return 3.14159 * self._radius ** 2
c = Circle(5)
print(c.radius) # 5
print(c.area) # 78.53975
c.radius = 10 # 触发 setter可以对一个函数应用多个装饰器,执行顺序从下到上(靠近函数的先执行)。
def bold(func):
def wrapper():
return f"<b>{func()}</b>"
return wrapper
def italic(func):
def wrapper():
return f"<i>{func()}</i>"
return wrapper
@bold
@italic
def text():
return "Hello"
print(text()) # <b><i>Hello</i></b>等价于:text = bold(italic(text))
顺序很重要!交换
@bold和@italic会改变 HTML 嵌套结构。
装饰器是不侵入原函数代码即可扩展其功能的强大工具
核心结构:外层函数接收原函数,内层 wrapper 执行增强逻辑
必须使用 *args, **kwargs 保证通用性
@decorator 是 func = decorator(func) 的语法糖
多装饰器按从下到上的顺序包装
内置装饰器 @staticmethod、@classmethod、@property 是面向对象编程的重要工具
装饰器广泛应用于框架开发、中间件、AOP(面向切面编程)等场景
如果一个装饰器没有正确返回 wrapper 函数,会发生什么?如何避免?
使用 functools.wraps 装饰 wrapper 有什么好处?请举例说明。
尝试编写一个装饰器 @retry(max_attempts=3),当被装饰的函数抛出异常时自动重试最多 3 次。