当前位置:首页 > Python > 正文内容

Python 装饰器完全指南:从入门到精通

admin8小时前Python3

什么是装饰器?

装饰器本质上是一个 Python 函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能。装饰器的返回值也是一个函数对象。

在 Python 中,装饰器使用 @decorator_name 的语法糖,这使得代码更加简洁和优雅。

基础装饰器示例

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

def my_decorator(func):
    def wrapper():
        print("在函数执行前做点什么")
        func()
        print("在函数执行后做点什么")
    return wrapper

@my_decorator
def say_hello():
    print("你好,世界!")

say_hello()

输出结果:

在函数执行前做点什么
你好,世界!
在函数执行后做点什么

在这个例子中,my_decorator 是一个装饰器,它接收一个函数作为参数,并返回一个新的函数 wrapper。当我们调用 say_hello() 时,实际上调用的是 wrapper() 函数。

处理带参数的函数

上面的装饰器只能处理没有参数的函数。如果我们需要装饰带有参数的函数,我们需要使用 *args 和 **kwargs 来接收任意参数:

def timing_decorator(func):
    def wrapper(*args, **kwargs):
        import time
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"{func.__name__} 执行时间: {end_time - start_time:.4f} 秒")
        return result
    return wrapper

@timing_decorator
def calculate_sum(n):
    total = 0
    for i in range(n):
        total += i
    return total

result = calculate_sum(1000000)
print(f"结果: {result}")

输出结果:

calculate_sum 执行时间: 0.0532 秒
结果: 499999500000

保留函数的元信息

当我们使用装饰器时,原始函数的元信息(如 __name__、__doc__ 等)会被包装函数替换。这可能会导致一些问题,比如文档字符串丢失。

Python 提供了 functools.wraps 装饰器来解决这个问题:

import functools

def debug_decorator(func):
    @functools.wraps(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

@debug_decorator
def add(a, b):
    """两个数相加"""
    return a + b

print(add(3, 5))
print(f"函数名: {add.__name__}")
print(f"文档: {add.__doc__}")

输出结果:

调用函数: add
参数: args=(3, 5), kwargs={}
返回值: 8
8
函数名: add
文档: 两个数相加

带参数的装饰器

有时我们需要装饰器本身接受参数。这需要创建一个装饰器工厂函数:

def repeat_decorator(times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            results = []
            for _ in range(times):
                result = func(*args, **kwargs)
                results.append(result)
            return results
        return wrapper
    return decorator

@repeat_decorator(times=3)
def get_random_number():
    import random
    return random.randint(1, 100)

numbers = get_random_number()
print(f"随机数列表: {numbers}")

输出示例:

随机数列表: [42, 78, 15]

类装饰器

除了函数装饰器,Python 还支持类装饰器。类装饰器可以实现更复杂的功能:

class CountCalls:
    def __init__(self, func):
        self.func = func
        self.count = 0
    
    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"函数 {self.func.__name__} 已被调用 {self.count} 次")
        return self.func(*args, **kwargs)

@CountCalls
def process_data(data):
    return data * 2

print(process_data(5))
print(process_data(10))

输出结果:

函数 process_data 已被调用 1 次
10
函数 process_data 已被调用 2 次
20

实际应用场景

1. 缓存装饰器

import functools

def cache_decorator(func):
    cache = {}
    
    @functools.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

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

print(fibonacci(10))
print(fibonacci(10))

2. 权限验证装饰器

def require_permission(permission):
    def decorator(func):
        def wrapper(*args, **kwargs):
            current_user = kwargs.get('user')
            if not current_user:
                raise PermissionError("未登录用户")
            
            if permission not in current_user.get('permissions', []):
                raise PermissionError(f"缺少权限: {permission}")
            
            return func(*args, **kwargs)
        return wrapper
    return decorator

@require_permission('admin')
def delete_user(user_id, user):
    print(f"删除用户: {user_id}")

admin_user = {'permissions': ['admin', 'read']}
delete_user(123, user=admin_user)

多个装饰器的叠加

我们可以为一个函数应用多个装饰器,装饰器的执行顺序是从下到上:

def decorator1(func):
    def wrapper():
        print("装饰器 1 - 前")
        func()
        print("装饰器 1 - 后")
    return wrapper

def decorator2(func):
    def wrapper():
        print("装饰器 2 - 前")
        func()
        print("装饰器 2 - 后")
    return wrapper

@decorator1
@decorator2
def my_function():
    print("执行函数")

my_function()

输出结果:

装饰器 1 - 前
装饰器 2 - 前
执行函数
装饰器 2 - 后
装饰器 1 - 后

最佳实践

1. 使用 functools.wraps:保留原始函数的元信息

2. 保持装饰器的简洁性:每个装饰器只做一件事

3. 处理异常:在装饰器中适当处理异常

4. 使用类型提示:提高代码可读性

总结

装饰器是 Python 中一个非常强大的特性,它让我们能够:

- 在不修改原有代码的情况下添加功能

- 提高代码的可重用性

- 实现横切关注点(如日志、缓存、权限验证)

掌握装饰器将帮助你写出更加优雅、简洁和可维护的 Python 代码。开始在你的项目中使用装饰器吧!

相关文章

[Python 教程] OpenCV 实战:图像与视频文件处理

OpenCV 实战:图像与视频文件处理本文详细介绍如何使用 OpenCV 处理图像和视频文件,包括读取、显示、保存等操作。一、图像文件操作1.1 读取图像import cv2 #&nb...

[Python 教程] Matplotlib 数据可视化教程

Matplotlib 数据可视化教程 Matplotlib 是 Python 最常用的绘图库。本文介绍常用图表的绘制方法。 一、基础设置 import matplotlib.pyplot as pl...

Python 装饰器实用技巧:从入门到精通

装饰器是 Python 最强大的特性之一,但也是很多开发者感到困惑的概念。简单来说,装饰器是一个函数,它接受另一个函数作为输入,并返回一个新的函数。使用装饰器,你可以在不修改原函数代码的情况下,为其添...

Python 上下文管理器:从入门到实战

在 Python 编程中,资源管理是一个永恒的话题。无论是打开文件、连接数据库,还是获取网络资源,我们都需要确保在使用完毕后正确释放这些资源。传统的 try-finally 模式虽然有效,但代码冗长且...

Python 上下文管理器的 5 个实用技巧,让你的代码更优雅

在 Python 编程中,上下文管理器(Context Manager)是一个优雅的资源管理工具。你可能已经熟悉最常见的用法——使用 with 语句打开文件,但上下文管理器的能力远不止于此。今天,我将...

Python 装饰器的高级应用与实战技巧

装饰器本质上是接受函数作为参数并返回新函数的高阶函数。理解这一点是掌握装饰器的关键。让我们从基础开始,逐步深入到高级应用。首先,我们需要理解函数在 Python 中是一等公民。这意味着函数可以像其他对...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。