折腾侠
技术教程

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

折腾侠
2026/04/21 发布
0约 9 分钟1555 字 / 1045 词00

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

引言

在 Python 编程的世界里,装饰器(Decorator)无疑是最具魅力也最令人困惑的特性之一。许多初学者第一次看到 INLINE_CODE_0 符号时都会感到困惑:这到底是什么?为什么函数上面要加这个?它又是如何工作的?

装饰器的本质是一个高阶函数——它接受一个函数作为输入,并返回一个新的函数。这种设计模式让你能够在不修改原始函数代码的前提下,为其添加额外的功能。这正是 Python 中"开放 - 封闭原则"(对扩展开放,对修改封闭)的完美体现。

本文将带你从零开始理解装饰器,通过大量可运行的代码示例,掌握这一强大工具,并学会在实际项目中灵活运用。

一、装饰器的基础概念

1.1 什么是装饰器?

简单来说,装饰器是一个"包装器"。想象你买了一件礼物,你会用包装纸把它包起来,添加一些装饰。礼物本身没有改变,但它现在有了额外的"功能"——更美观、更适合送礼。

装饰器对函数的作用也是如此:它不改变函数的核心逻辑,但可以为函数添加额外的行为,比如:

  • 记录日志
  • 性能计时
  • 权限验证
  • 缓存结果
  • 异常处理

1.2 从函数开始理解

要理解装饰器,首先需要理解三个概念:

1. 函数是一等公民

在 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!

2. 嵌套函数

函数内部可以定义另一个函数:

Python
def outer():
    print("这是外层函数")
    
    def inner():
        print("这是内层函数")
    
    return inner

func = outer()
func()  # 输出:这是内层函数

3. 闭包

内层函数可以访问外层函数的变量:

Python
def make_multiplier(factor):
    def multiply(number):
        return number * factor
    return multiply

double = make_multiplier(2)
triple = make_multiplier(3)

print(double(5))  # 输出:10
print(triple(5))  # 输出:15

理解了这三点,你就已经掌握了装饰器的基础!

二、手写第一个装饰器

2.1 最简单的装饰器

让我们从一个最简单的例子开始:

Python
def my_decorator(func):
    def wrapper():
        print("函数执行前")
        func()
        print("函数执行后")
    return wrapper

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

# 手动应用装饰器
say_hello = my_decorator(say_hello)
say_hello()

输出:

函数执行前
Hello!
函数执行后

2.2 使用 @ 语法糖

Python 提供了更简洁的语法:

Python
def my_decorator(func):
    def wrapper():
        print("函数执行前")
        func()
        print("函数执行后")
    return wrapper

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

say_hello()

INLINE_CODE_1 等价于 INLINE_CODE_2

2.3 处理函数参数

实际应用中,函数通常有参数。装饰器需要能够处理任意参数:

Python
def my_decorator(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

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

@my_decorator
def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

add(3, 5)  # 输出完整的调用信息
greet("Alice", greeting="Hi")

2.4 保留函数元信息

使用装饰器后,函数的 INLINE_CODE_3INLINE_CODE_4 会被 wrapper 覆盖。使用 INLINE_CODE_5 可以保留原始信息:

Python
from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def documented_function():
    """这是一个有文档的函数"""
    pass

print(documented_function.__name__)  # 输出:documented_function
print(documented_function.__doc__)   # 输出:这是一个有文档的函数

三、实用装饰器示例

3.1 日志装饰器

记录函数的调用信息,便于调试和监控:

Python
from functools import wraps
import logging

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')

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

@log_call
def divide(a, b):
    return a / b

divide(10, 2)
# divide(10, 0)  # 会记录异常

3.2 性能计时装饰器

测量函数执行时间,帮助定位性能瓶颈:

Python
from functools import wraps
import time

def timer(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

@timer
def slow_function():
    time.sleep(1)

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

slow_function()
fibonacci(10)

3.3 缓存装饰器(Memoization)

缓存函数结果,避免重复计算:

Python
from functools import wraps

def memoize(func):
    cache = {}
    
    @wraps(func)
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
            print(f"计算 {args} 并缓存结果")
        else:
            print(f"从缓存获取 {args} 的结果")
        return cache[args]
    return wrapper

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

print(fibonacci(5))  # 会计算并缓存
print(fibonacci(5))  # 直接从缓存获取
print(fibonacci(6))  # 只需要计算新值

3.4 重试装饰器

在网络请求等不稳定操作中自动重试:

Python
from functools import wraps
import time
import random

def retry(max_attempts=3, delay=1):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            last_exception = None
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    last_exception = e
                    print(f"尝试 {attempt + 1}/{max_attempts} 失败:{e}")
                    if attempt < max_attempts - 1:
                        time.sleep(delay)
            raise last_exception
        return wrapper
    return decorator

@retry(max_attempts=3, delay=0.5)
def unstable_operation():
    if random.random() < 0.7:  # 70% 概率失败
        raise ConnectionError("网络不稳定")
    return "操作成功"

try:
    result = unstable_operation()
    print(result)
except Exception as e:
    print(f"最终失败:{e}")

3.5 权限验证装饰器

在 Web 应用中验证用户权限:

Python
from functools import wraps

class User:
    def __init__(self, name, role):
        self.name = name
        self.role = role

# 模拟当前用户
current_user = None

def require_role(*allowed_roles):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            if current_user is None:
                raise PermissionError("用户未登录")
            if current_user.role not in allowed_roles:
                raise PermissionError(
                    f"需要权限:{allowed_roles}, 当前权限:{current_user.role}"
                )
            return func(*args, **kwargs)
        return wrapper
    return decorator

@require_role("admin", "manager")
def delete_user(user_id):
    print(f"删除用户 {user_id}")

@require_role("admin")
def shutdown_system():
    print("系统关闭")

# 测试
current_user = User("Alice", "admin")
delete_user(123)  # 成功
shutdown_system()  # 成功

current_user = User("Bob", "user")
# delete_user(456)  # 抛出权限错误

四、带参数的装饰器

有时候装饰器本身需要参数,比如指定重试次数、缓存时间等。这需要三层嵌套:

Python
from functools import wraps

def repeat(times):
    """重复执行函数指定次数"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            results = []
            for _ in range(times):
                results.append(func(*args, **kwargs))
            return results
        return wrapper
    return decorator

@repeat(3)
def roll_dice():
    import random
    return random.randint(1, 6)

print(roll_dice())  # 输出 3 个随机数的列表

记忆技巧

  • 无参数装饰器:2 层函数(decorator + wrapper)
  • 有参数装饰器:3 层函数(param_decorator + decorator + wrapper)

五、类装饰器

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

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

@singleton
class Database:
    def __init__(self):
        self.connection = "数据库连接"
        print("创建数据库连接")

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

六、装饰器栈(多个装饰器)

一个函数可以应用多个装饰器,从下往上执行:

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 - 后

七、实际项目中的应用场景

7.1 Web 框架中的装饰器

Flask 和 Django 等框架广泛使用装饰器:

Python
# Flask 路由装饰器
@app.route("/users/<int:user_id>")
@require_login
@cache_response(timeout=300)
def get_user(user_id):
    return db.get_user(user_id)

7.2 API 速率限制

Python
from functools import wraps
import time
from collections import defaultdict

def rate_limit(max_calls, period):
    calls = defaultdict(list)
    
    def decorator(func):
        @wraps(func)
        def wrapper(client_id, *args, **kwargs):
            now = time.time()
            # 清理过期记录
            calls[client_id] = [t for t in calls[client_id] if now - t < period]
            
            if len(calls[client_id]) >= max_calls:
                raise Exception("请求频率超限")
            
            calls[client_id].append(now)
            return func(client_id, *args, **kwargs)
        return wrapper
    return decorator

@rate_limit(max_calls=5, period=60)  # 每分钟最多 5 次
def api_call(client_id, endpoint):
    print(f"调用 API: {endpoint}")

7.3 事务管理

Python
from functools import wraps

def transaction(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("BEGIN TRANSACTION")
        try:
            result = func(*args, **kwargs)
            print("COMMIT")
            return result
        except Exception as e:
            print(f"ROLLBACK: {e}")
            raise
    return wrapper

@transaction
def transfer_money(from_account, to_account, amount):
    print(f"从 {from_account} 转账 {amount}{to_account}")
    # 如果中间出错,会自动回滚

八、最佳实践与注意事项

8.1 始终使用 @wraps

忘记使用 INLINE_CODE_6 是常见错误,会导致调试困难:

Python
# 错误示例
def bad_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

# 正确示例
def good_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

8.2 保持装饰器单一职责

每个装饰器只做一件事,便于组合和维护:

Python
# 不好:一个装饰器做太多事
@log_and_cache_and_retry
def my_function():
    pass

# 好:多个单一职责的装饰器
@log_call
@cache_result
@retry_on_failure
def my_function():
    pass

8.3 注意装饰器的性能开销

复杂装饰器可能影响性能,在热点代码中谨慎使用:

Python
# 避免在循环中重复应用复杂装饰器
for i in range(1000000):
    decorated_function(i)  # 每次调用都有装饰器开销

结语

装饰器是 Python 中最优雅的特性之一,它让你能够以声明式的方式为函数添加功能,保持代码的整洁和可维护性。

本文从基础概念讲起,通过大量可运行的示例,展示了装饰器的各种应用场景。希望你能在实践中灵活运用这些技巧,写出更优雅的 Python 代码。

记住:装饰器的核心思想是不修改原有代码,通过包装来扩展功能。这正是软件设计中"开放 - 封闭原则"的完美体现。

下一步,你可以尝试:

  1. 为自己的项目创建自定义装饰器
  2. 阅读 Flask、Django 等框架的源码,学习它们如何使用装饰器
  3. 探索 INLINE_CODE_7 模块中的内置装饰器(如 INLINE_CODE_8INLINE_CODE_9

Happy Coding!

分享到:

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

加载评论中...