Python 中的装饰器

装饰器是 Python 中修改或增强函数行为 的一种设计模式,它可以在不修改原函数代码 的情况下,为函数添加额外功能。装饰器本质上是一个高阶函数,接收一个函数作为参数,返回一个新的函数。

一、装饰器

1. 为什么需要装饰器?

复制代码
# 原始需求:计算函数执行时间

# 问题:每个函数都要重复写计时代码
def func1():
    start = time.time()
    # ... 函数逻辑
    end = time.time()
    print(f"耗时: {end - start}")

def func2():
    start = time.time()
    # ... 函数逻辑
    end = time.time()
    print(f"耗时: {end - start}")

# 装饰器解决方案:复用计时逻辑
def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} 耗时: {end - start:.2f}s")
        return result
    return wrapper

@timer
def func1():
    pass

@timer
def func2():
    pass

2. 装饰器的本质

复制代码
# 装饰器语法糖
@decorator
def func():
    pass

# 等价于
func = decorator(func)

二、函数装饰器

1. 无参数装饰器

复制代码
def logger(func):
    """记录函数调用的装饰器"""
    def wrapper(*args, **kwargs):
        print(f"调用函数: {func.__name__}")
        print(f"参数: args={args}, kwargs={kwargs}")
        result = func(*args, **kwargs)
        print(f"返回值: {result}")
        return result
    return wrapper

@logger
def add(a, b):
    return a + b

add(3, 5)
# 输出:
# 调用函数: add
# 参数: args=(3, 5), kwargs={}
# 返回值: 8

2. 带参数的装饰器

复制代码
def repeat(times):
    """重复执行函数的装饰器"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            results = []
            for i in range(times):
                print(f"第 {i+1} 次执行")
                results.append(func(*args, **kwargs))
            return results
        return wrapper
    return decorator

@repeat(3)
def greet(name):
    return f"Hello, {name}"

result = greet("Alice")
print(result)  # ['Hello, Alice', 'Hello, Alice', 'Hello, Alice']

3. 多层装饰器

复制代码
def bold(func):
    def wrapper(*args, **kwargs):
        return f"<b>{func(*args, **kwargs)}</b>"
    return wrapper

def italic(func):
    def wrapper(*args, **kwargs):
        return f"<i>{func(*args, **kwargs)}</i>"
    return wrapper

@bold
@italic
def greet(name):
    return f"Hello, {name}"

print(greet("Alice"))  # <b><i>Hello, Alice</i></b>

# 执行顺序:从下往上
# greet = bold(italic(greet))

三、类装饰器

1. 类作为装饰器

复制代码
class CountCalls:
    """统计函数调用次数的装饰器"""
    def __init__(self, func):
        self.func = func
        self.count = 0
    
    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"调用次数: {self.count}")
        return self.func(*args, **kwargs)

@CountCalls
def say_hello():
    print("Hello!")

say_hello()  # 调用次数: 1 \n Hello!
say_hello()  # 调用次数: 2 \n Hello!

2. 带参数的类装饰器

复制代码
class Repeat:
    def __init__(self, times):
        self.times = times
    
    def __call__(self, func):
        def wrapper(*args, **kwargs):
            results = []
            for i in range(self.times):
                results.append(func(*args, **kwargs))
            return results
        return wrapper

@Repeat(3)
def greet(name):
    return f"Hello, {name}"

print(greet("Alice"))  # ['Hello, Alice', 'Hello, Alice', 'Hello, Alice']

四、常用内置装饰器

1. @staticmethod@classmethod

复制代码
class MyClass:
    class_var = "类变量"
    
    @staticmethod
    def static_method():
        """静态方法:不需要 self 或 cls"""
        return "静态方法"
    
    @classmethod
    def class_method(cls):
        """类方法:第一个参数是类本身"""
        return f"类方法,访问 {cls.class_var}"
    
    def instance_method(self):
        """实例方法:第一个参数是实例"""
        return "实例方法"

print(MyClass.static_method())  # 静态方法
print(MyClass.class_method())   # 类方法,访问 类变量

2. @property

复制代码
class Circle:
    def __init__(self, radius):
        self._radius = radius
    
    @property
    def radius(self):
        """getter"""
        return self._radius
    
    @radius.setter
    def radius(self, value):
        """setter"""
        if value <= 0:
            raise ValueError("半径必须大于0")
        self._radius = value
    
    @property
    def area(self):
        """只读属性"""
        return 3.14159 * self._radius ** 2

c = Circle(5)
print(c.radius)  # 5
c.radius = 10
print(c.area)    # 314.159

3. @functools.wraps

保留原函数的元数据(名称、文档字符串等)。

复制代码
import functools

def my_decorator(func):
    @functools.wraps(func)  # 保留 func 的元信息
    def wrapper(*args, **kwargs):
        """包装函数"""
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def greet(name):
    """问候函数"""
    return f"Hello, {name}"

print(greet.__name__)  # greet(有 @wraps)
print(greet.__doc__)   # 问候函数(有 @wraps)

# 如果没有 @wraps,会输出 wrapper 和 包装函数

五、装饰器的执行顺序

复制代码
def decorator_a(func):
    print("装饰器 A 开始")
    def wrapper(*args, **kwargs):
        print("调用 A")
        return func(*args, **kwargs)
    print("装饰器 A 结束")
    return wrapper

def decorator_b(func):
    print("装饰器 B 开始")
    def wrapper(*args, **kwargs):
        print("调用 B")
        return func(*args, **kwargs)
    print("装饰器 B 结束")
    return wrapper

@decorator_a
@decorator_b
def my_func():
    print("原函数")

my_func()

装饰器 B 开始
装饰器 B 结束
装饰器 A 开始
装饰器 A 结束
调用 A
调用 B
原函数

执行顺序

  1. 装饰阶段 :从下往上(decorator_b 先执行)

  2. 调用阶段 :从上往下(wrapper_awrapper_b → 原函数)

核心要点

  1. 装饰器本质是高阶函数:接收函数,返回函数

  2. @decorator 等价于 func = decorator(func)

  3. 多个装饰器:装饰时从下往上 ,调用时从上往下

  4. 总是用 @functools.wraps 保留原函数元信息

  5. 类装饰器通过 __call__ 方法实现

相关推荐
我叫唧唧波2 小时前
Python+AI 全栈学习笔记
人工智能·python·学习
copyer_xyf2 小时前
Python 异常处理
前端·后端·python
麻雀飞吧3 小时前
期货多合约策略目标持仓怎么更新才不乱
python·区块链
Cthy_hy3 小时前
拓扑排序超详解:原理 + Kahn 贪心算法
python·算法·贪心算法
LSssT.3 小时前
【01】Python 机器学习
开发语言·python
为爱停留3 小时前
给智能体装上「刹车」:中断(Interrupts)与人工审批全解析
python
l1t4 小时前
DeepSeek总结的使用实体-组件-系统和基于存在性处理进行Python编程39-40
开发语言·python
曾阿伦4 小时前
Python 搭建简易HTTP服务
开发语言·python·http
MIUMIUKK4 小时前
从语法层面,看懂 Python 的特殊处
java·开发语言·python