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__ 方法实现

相关推荐
青 春 记 忆9 小时前
Dify Docker Compose 通用无损升级指南:从备份、双版本预演到切换与回滚
运维·人工智能·python·docker·容器
GlueNa2SiO39 小时前
03-Flask模板引擎Jinja2详解
笔记·python·flask
临沂GEO12 小时前
GEO搜索优化科普|正规地理位置流量运营入门指南
大数据·人工智能·python·流量运营
大模型码小白12 小时前
Spring AI Tool 实现自然语言操作 MySQL 数据库详解
服务器·开发语言·数据库·人工智能·python·mysql·spring
荷蒲13 小时前
【小白量化Qbuddy】利用AI学习中文Python
人工智能·python·学习
数据狐(Datafox)15 小时前
京东商品列表API技术解析与落地应用(含标准 JSON 示例)
java·大数据·前端·人工智能·python·数据分析·json
量化吞吐机15 小时前
2026年下半年手工交易规则走向量化表达,产品该先接住哪一步
人工智能·python
Metaphor69215 小时前
使用 Python 读取和删除 Excel 文件属性
python·excel
迷迭香yy16 小时前
Python构建转融券多空识别系统从接口到因子的全流程 IG50免费开源股票数据API接口
开发语言·python·开源
月光船幽幽17 小时前
真实即粗糙,光滑是伪造
人工智能·python