折腾侠
技术教程

Python 装饰器完全指南:从入门到实战应用

折腾侠
2026/04/24 发布
0约 7 分钟1103 字 / 726 词00

Python 装饰器完全指南:从入门到实战应用

引言

在 Python 编程中,装饰器(Decorator)是最强大也最容易被误解的特性之一。许多初学者在看到 INLINE_CODE_0 符号时感到困惑,而经验丰富的开发者则利用装饰器编写出优雅、可维护的代码。本文将带你从装饰器的基本原理出发,逐步掌握其高级用法,并通过实际案例展示如何在真实项目中应用这一强大工具。

装饰器的本质是一个函数,它接受另一个函数作为参数,并返回一个新的函数。通过装饰器,我们可以在不修改原函数代码的前提下,为其添加额外的功能。这种设计模式完美体现了 Python 的"开放 - 封闭原则"——对扩展开放,对修改封闭。

一、装饰器的基础原理

1.1 函数即对象

要理解装饰器,首先要理解 Python 中"函数是一等公民"的概念。在 Python 中,函数可以像其他对象一样被传递、赋值和返回:

Python
def greet(name):
    return f"Hello, {name}!"

# 函数可以赋值给变量
say_hello = greet
print(say_hello("Alice"))  # 输出:Hello, Alice!

# 函数可以作为参数传递
def call_function(func, arg):
    return func(arg)

print(call_function(greet, "Bob"))  # 输出:Hello, Bob!

# 函数可以作为返回值
def create_greeter(prefix):
    def greeter(name):
        return f"{prefix}, {name}!"
    return greeter

say_hi = create_greeter("Hi")
print(say_hi("Charlie"))  # 输出:Hi, Charlie!

1.2 第一个装饰器

基于上述概念,我们可以创建最简单的装饰器:

Python
def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("函数执行前")
        result = func(*args, **kwargs)
        print("函数执行后")
        return result
    return wrapper

@my_decorator
def say_hello(name):
    print(f"Hello, {name}!")

say_hello("Alice")
# 输出:
# 函数执行前
# Hello, Alice!
# 函数执行后

INLINE_CODE_1 语法糖等价于 INLINE_CODE_2。装饰器在函数定义时立即执行,返回的 INLINE_CODE_3 函数替换了原函数。

1.3 保留函数元信息

上述装饰器有一个问题:原函数的 INLINE_CODE_4INLINE_CODE_5 会被 wrapper 覆盖。使用 INLINE_CODE_6 可以解决这个问题:

Python
from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("函数执行前")
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def say_hello(name):
    """向用户打招呼"""
    print(f"Hello, {name}!")

print(say_hello.__name__)  # 输出:say_hello(而不是 wrapper)
print(say_hello.__doc__)   # 输出:向用户打招呼

二、带参数的装饰器

2.1 装饰器工厂

当装饰器本身需要参数时,我们需要创建"装饰器工厂"——一个返回装饰器的函数:

Python
def repeat(times):
    """重复执行函数的装饰器"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for i in range(times):
                print(f"第 {i + 1} 次执行")
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

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

greet("Alice")

2.2 可选参数的装饰器

更复杂的场景是装饰器既有可选参数又有默认行为:

Python
def optional_decorator(_func=None, *, prefix="INFO"):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print(f"[{prefix}] 开始执行 {func.__name__}")
            return func(*args, **kwargs)
        return wrapper
    
    if _func is None:
        return decorator
    else:
        return decorator(_func)

@optional_decorator
def func1():
    pass

@optional_decorator(prefix="DEBUG")
def func2():
    pass

三、类装饰器

装饰器不仅可以装饰函数,还可以装饰类:

Python
def singleton(cls):
    """单例模式装饰器"""
    instances = {}
    @wraps(cls)
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return get_instance

@singleton
class Database:
    def __init__(self):
        self.connection = "DB Connection"

db1 = Database()
db2 = Database()
print(db1 is db2)  # 输出:True(同一个实例)

四、实际应用场景

4.1 日志记录

Python
import logging
from datetime import datetime

logging.basicConfig(level=logging.INFO)

def log_execution(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        logging.info(f"[{datetime.now()}] 调用 {func.__name__}")
        try:
            result = func(*args, **kwargs)
            logging.info(f"[{datetime.now()}] {func.__name__} 执行成功")
            return result
        except Exception as e:
            logging.error(f"[{datetime.now()}] {func.__name__} 执行失败:{e}")
            raise
    return wrapper

@log_execution
def process_data(data):
    return [x * 2 for x in data]

4.2 性能分析

Python
import time
from functools import wraps

def timing(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()
        print(f"{func.__name__} 执行时间:{end - start:.6f} 秒")
        return result
    return wrapper

@timing
def slow_function():
    time.sleep(1)
    return "完成"

4.3 权限验证

Python
def require_auth(func):
    @wraps(func)
    def wrapper(user, *args, **kwargs):
        if not user.is_authenticated:
            raise PermissionError("用户未认证")
        if not user.has_permission(func.__name__):
            raise PermissionError("用户无权限")
        return func(user, *args, **kwargs)
    return wrapper

@require_auth
def delete_resource(user, resource_id):
    # 删除资源
    pass

4.4 缓存(记忆化)

Python
def memoize(func):
    cache = {}
    @wraps(func)
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrapper

@memoize
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(50))  # 瞬间完成

4.5 重试机制

Python
import random
import time

def retry(max_attempts=3, delay=1, backoff=2):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            attempts = 0
            current_delay = delay
            while attempts < max_attempts:
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    attempts += 1
                    if attempts >= max_attempts:
                        raise
                    print(f"尝试 {attempts}/{max_attempts} 失败,{current_delay}秒后重试")
                    time.sleep(current_delay)
                    current_delay *= backoff
        return wrapper
    return decorator

@retry(max_attempts=5, delay=2)
def unstable_api():
    if random.random() < 0.7:
        raise ConnectionError("网络错误")
    return "成功"

4.6 事务管理

Python
def transaction(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 开始事务
        print("BEGIN TRANSACTION")
        try:
            result = func(*args, **kwargs)
            print("COMMIT")
            return result
        except Exception:
            print("ROLLBACK")
            raise
    return wrapper

@transaction
def transfer_money(from_account, to_account, amount):
    # 转账逻辑
    pass

五、装饰器栈

多个装饰器可以叠加使用,执行顺序是从下到上(从内到外):

Python
def decorator_a(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("A: 前")
        result = func(*args, **kwargs)
        print("A: 后")
        return result
    return wrapper

def decorator_b(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("B: 前")
        result = func(*args, **kwargs)
        print("B: 后")
        return result
    return wrapper

@decorator_a
@decorator_b
def hello():
    print("Hello!")

hello()
# 输出顺序:
# A: 前
# B: 前
# Hello!
# B: 后
# A: 后

六、最佳实践与注意事项

6.1 始终使用 @wraps

忘记使用 INLINE_CODE_7 是常见错误,会导致调试困难和文档丢失。

6.2 保持装饰器单一职责

每个装饰器只做一件事,复杂的装饰器应该拆分成多个小装饰器组合使用。

6.3 注意装饰器执行时机

装饰器在模块导入时执行,不要在装饰器中执行耗时操作。

6.4 处理异步函数

对于异步函数,需要使用 INLINE_CODE_8 相关的装饰器:

Python
import asyncio
from functools import wraps

def async_timing(func):
    @wraps(func)
    async def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = await func(*args, **kwargs)
        end = time.perf_counter()
        print(f"{func.__name__} 执行时间:{end - start:.6f} 秒")
        return result
    return wrapper

@async_timing
async def fetch_data():
    await asyncio.sleep(1)
    return "data"

结语

装饰器是 Python 中最优雅的特性之一,它让我们能够以声明式的方式为函数添加功能,极大地提高了代码的可读性和可维护性。从简单的日志记录到复杂的权限控制,从性能优化到事务管理,装饰器的应用场景无处不在。

掌握装饰器的关键在于理解其本质——高阶函数的应用。一旦你理解了函数可以作为参数传递和返回,装饰器的神秘面纱就会被揭开。建议从简单的装饰器开始练习,逐步尝试更复杂的场景,最终你将能够灵活运用装饰器编写出更加 Pythonic 的代码。

记住:优秀的装饰器应该是透明的、可组合的、并且遵循单一职责原则。当你发现自己在多个函数中重复相同的代码模式时,就是考虑使用装饰器的最佳时机。

分享到:

如果这篇文章对你有帮助,欢迎请作者喝杯咖啡 ☕

加载评论中...