Python 装饰器从入门到实战

引言

装饰器是 Python 中最优雅、最强大的特性之一。它允许你在不修改原函数代码的情况下,为函数添加额外功能。本文将带你从基础语法到实战应用,全面掌握装饰器。

一、装饰器基础语法

装饰器本质上是一个接受函数作为参数并返回新函数的高阶函数。

python 复制代码
def my_decorator(func):
    def wrapper():
        print("函数执行前")
        func()
        print("函数执行后")
    return wrapper

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

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

二、4 个实用装饰器示例

1. 计时装饰器

python 复制代码
import time
from functools import wraps

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

@timer
def slow_function():
    time.sleep(2)
    return "完成"

slow_function()

2. 重试装饰器

python 复制代码
from functools import wraps

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

@retry(max_attempts=3)
def fetch_data():
    # 模拟可能失败的网络请求
    import random
    if random.random() < 0.7:
        raise ConnectionError("网络错误")
    return "数据获取成功"

3. 缓存装饰器

less 复制代码
from functools import wraps, lru_cache

def memoize(func):
    cache = {}
    @wraps(func)
    def wrapper(*args):
        if args in cache:
            print(f"命中缓存:{args}")
            return cache[args]
        result = func(*args)
        cache[args] = result
        return result
    return wrapper

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

print(fibonacci(10))  # 55
print(fibonacci(10))  # 直接从缓存返回

4. 权限验证装饰器

python 复制代码
from functools import wraps

def require_login(func):
    @wraps(func)
    def wrapper(user, *args, **kwargs):
        if not user.get('is_logged_in'):
            raise PermissionError("用户未登录")
        return func(user, *args, **kwargs)
    return wrapper

@require_login
def view_profile(user):
    return f"欢迎,{user['name']}!"

# 测试
user = {'name': '张三', 'is_logged_in': True}
print(view_profile(user))  # 欢迎,张三!

三、带参数的装饰器

python 复制代码
from functools import wraps

def repeat(times):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(times=3)
def greet(name):
    print(f"你好,{name}!")

greet("世界")

四、多装饰器执行顺序

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 test():
    print(">>> 函数执行 <<<")

test()
# 执行顺序:
# [A] 前置处理
# [B] 前置处理
# >>> 函数执行 <<<
# [B] 后置处理
# [A] 后置处理

五、最佳实践

  1. 使用 @wraps 保留原函数信息
python 复制代码
from functools import wraps

def my_decorator(func):
    @wraps(func)  # 保留 __name__, __doc__ 等
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper
  1. 装饰器类实现
ruby 复制代码
class CountCalls:
    def __init__(self, func):
        wraps(func)(self)
        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 hello():
    print("Hello")
  1. 避免过度嵌套 - 超过 3 层装饰器时考虑重构

总结

装饰器是 Python 函数式编程的核心工具,掌握它能让你写出更优雅、可复用的代码。从简单的日志记录到复杂的权限控制,装饰器的应用场景无处不在。

关键点回顾:

  • 装饰器 = 高阶函数(接收函数,返回函数)
  • 使用 @wraps 保留原函数元数据
  • 带参数装饰器需要三层嵌套
  • 多个装饰器从下往上执行
相关推荐
李昊哲小课1 天前
Python办公自动化教程 - 第7章 综合实战案例 - 企业销售管理系统
开发语言·python·数据分析·excel·数据可视化·openpyxl
不知名的老吴1 天前
返回None还是空集合?防御式编程的关键细节
开发语言·python
李昊哲小课1 天前
Python办公自动化教程 - 第5章 图表创建 - 让数据可视化
python·信息可视化·数据分析·数据可视化·openpyxl
chushiyunen1 天前
python pygame实现贪食蛇
开发语言·python·pygame
Dream of maid1 天前
Python-基础2(流程控制)
python
Lenyiin1 天前
《Python 修炼全景指南:一》从环境搭建到第一个程序
开发语言·python
涛声依旧393161 天前
Python项目实战:学生信息管理系统
开发语言·python·数据挖掘
kcuwu.1 天前
Python进阶:生成器与协程,高效并发编程的核心实践
windows·python·php
XiaoQiao6669991 天前
python 简单题目练手【详解版】【1】
开发语言·python
ZC跨境爬虫1 天前
极验滑动验证码自动化实战:背景提取、缺口定位与Playwright滑动模拟
前端·爬虫·python·自动化