Python装饰器居然还能这么用!新手小白必看[附源码]

90 阅读7分钟

大家好呀!我是花姐。你有没有遇到过这样的情况:你写了一个函数,突然想到要给它增加一些额外功能,或者你希望能在多个地方重复使用这段功能,结果却发现每次都得重新写一遍代码,超级烦!😂

image.png 别担心,今天花姐给大家介绍一个神器——装饰器!它可以在不改变原始函数代码的情况下,为函数增加新功能,而且代码更加简洁、高效,关键是让你的代码更“优雅”。🎉

好啦,今天我们就来一起看看装饰器的魔力,它怎么帮助我们做出更简洁、可复用的代码!

1. 日志记录:让代码更“有迹可循”

在开发过程中,我们常常需要追踪代码的执行情况,或者排查一些bug。日志记录就是解决这个问题的好帮手!通过装饰器,我们可以在不改变函数内容的情况下,给函数加上日志功能。这样就能省去每个函数都写日志的麻烦啦!🌟

import logging

# 设置日志基本配置
logging.basicConfig(level=logging.INFO)

def log_decorator(func):
    def wrapper(*args, **kwargs):
        # 记录日志:输出函数名称和参数
        logging.info(f"Running '{func.__name__}' with arguments {args} and {kwargs}")
        # 执行原始函数,并返回结果
        return func(*args, **kwargs)
    return wrapper

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

say_hello("Alice")

代码解释:

  1. logging.basicConfig(level=logging.INFO)
    这行代码设置了日志系统的基础配置,指定日志级别为 INFO,这样才能将日志信息(包括 logging.info)输出到控制台。

  2. 定义装饰器 log_decorator
    这段代码首先定义了一个名为 log_decorator 的装饰器。装饰器本质上是一个函数,它接受一个函数 func 作为参数,并返回一个新的函数 wrapper,这个 wrapper 函数会包装原始函数,添加额外的功能。

  3. wrapper 函数:
    wrapper 是装饰器的核心部分,它会在执行原始函数之前,先记录日志。argskwargs 代表传递给原始函数的位置参数和关键字参数。通过 logging.info,我们记录了函数的名称和传入的参数。

  4. 应用装饰器:
    通过 @log_decorator,我们将 say_hello 函数装饰了一下,意味着每次调用 say_hello 时,都会先执行 log_decorator 里的 wrapper,自动记录日志。

每当调用 say_hello 函数时,都会输出类似以下的日志信息:

INFO:root:Running 'say_hello' with arguments ('Alice',) and {}
Hello, Alice!

这样,我们的代码就变得更容易调试和追踪了!🎯


2. 性能测试:优化代码更轻松

在开发中,我们常常需要检查哪些部分运行得慢,以便进行优化。装饰器在这个场景下特别有用,它可以自动记录每个函数的执行时间,帮助我们找出性能瓶颈。⏱️

import time

def timing_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()  # 记录开始时间
        result = func(*args, **kwargs)  # 执行原始函数
        end_time = time.time()  # 记录结束时间
        print(f"{func.__name__} took {end_time - start_time:.4f} seconds to run.")
        return result
    return wrapper

@timing_decorator
def slow_function(delay_time):
    time.sleep(delay_time)

slow_function(2)

代码解释:

  1. 定义装饰器 timing_decorator
    timing_decorator 用来计算并输出函数的执行时间。首先,我们在 wrapper 函数里记录下函数执行前的时间(start_time),然后调用原始函数 func(*args, **kwargs),最后记录结束时间(end_time)。

  2. 计算执行时间:
    end_time - start_time 就是函数执行的总时间,然后我们通过 print 输出这段时间,帮助我们知道函数执行的快慢。

  3. 装饰函数:
    通过 @timing_decorator,我们将 slow_function 装饰,表示每次调用它时,都自动计算并输出执行时间。比如,调用 slow_function(2) 会输出:

slow_function took 2.0000 seconds to run.

这样,我们就可以很方便地找出性能瓶颈,优化代码啦!⚡


3. 结果缓存:节省计算时间

对于一些计算量大的函数,尤其是那些输入和输出不变的函数,重复计算会浪费大量时间和资源。通过装饰器缓存计算结果,可以大大提高程序的效率!💨

from functools import lru_cache

@lru_cache(maxsize=32)
def fib(n):
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)

print(fib(10))

代码解释:

  1. 使用 lru_cache
    我们使用 @lru_cache 装饰器来缓存 Fibonacci 数列的计算结果。maxsize=32 参数表示缓存最多保存32个结果,超出后会自动删除最久未使用的缓存项。

  2. 缓存机制:
    lru_cache 让我们避免了重复计算相同的输入值。例如,当我们计算 fib(10) 时,程序会自动缓存计算过程中的中间结果,之后再计算相同的数字时,直接从缓存中读取,节省了大量的时间。

每次调用 fib(10) 时,计算速度会显著提高。🎉


4. 权限验证:轻松管理权限

在Web开发中,验证用户权限是很常见的需求。通过装饰器,我们可以轻松实现权限验证,让代码更加整洁,逻辑分明。👮‍♀️

from functools import wraps

def permission_required(permission):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            user_permission = kwargs.get('user_permission')
            if user_permission != permission:
                raise Exception("Permission denied")
            return func(*args, **kwargs)
        return wrapper
    return decorator

@permission_required('admin')
def delete_user(user_id, user_permission):
    print(f"User {user_id} has been deleted.")

delete_user(1, user_permission='admin')  # 成功
delete_user(1, user_permission='guest')  # 抛出异常: Permission denied

代码解释:

  1. permission_required 装饰器:
    permission_required 是一个装饰器工厂,它接受一个 permission 参数,表示所需的权限。然后我们定义一个内部装饰器 decorator,它会检查当前用户是否拥有正确的权限。

  2. wrapper 函数:
    wrapper 中,我们通过 kwargs.get('user_permission') 获取用户的权限。如果权限不匹配,就会抛出 Permission denied 异常;否则,调用原始函数。

  3. 应用装饰器:
    当我们使用 @permission_required('admin') 装饰 delete_user 函数时,每次调用 delete_user 时,都会先检查权限。如果权限不对,抛出异常。


5. 参数验证:防止参数出错

装饰器还能用于验证函数的参数,确保它们符合预期。这样能避免函数执行过程中出现参数错误,提升代码的健壮性。🛡️

from functools import wraps

def type_check(correct_type):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            if not all([isinstance(arg, correct_type) for arg in args]):
                raise ValueError("Incorrect argument type")
            return func(*args, **kwargs)
        return wrapper
    return decorator

@type_check(str)  #用于检查输入的参数是否都为str字符串格式
def string_add(str1, str2):
    return str1 + str2

print(string_add("hello", "3"))  # 参数符合要求都是字符串
print(string_add("hello", 3))  # 参数不符合要求

代码解释:

  1. type_check 装饰器:
    type_check 装饰器用于检查函数的参数类型。我们通过 isinstance 检查每个参数是否是正确的类型。如果发现参数类型不对,就会抛出一个 ValueError

  2. 验证过程:

    wrapper 函数中,all([isinstance(arg, correct_type) for arg in args]) 用来检查所有位置参数是否符合预期类型。如果有任何一个参数不符合,就抛出异常。


6.functools简介

functools 是 Python 标准库中的一个模块,提供了一些非常实用的工具和装饰器,主要用于处理高阶函数(即以函数作为参数或返回值的函数)。通过 functools,我们可以更方便地处理函数行为,优化代码结构,并提高可读性和性能。

functools 常用功能和方法

1. functools.wraps

作用: 用于装饰器内部,保持被装饰函数的元信息(如函数名、文档字符串等)。

2. functools.lru_cache

作用: 为函数提供缓存功能,加速重复调用时的执行速度。

3. functools.partial

作用: 用于固定函数的部分参数,返回一个新的函数。

4. functools.reduce

作用: 对一个序列进行累计计算。

5. functools.singledispatch

作用: 实现单分派泛型函数,允许根据第一个参数的类型选择函数。

6. functools.total_ordering

作用: 通过实现少量比较方法,为类提供完整的排序支持。

functools 就像是 Python 的百宝箱,为开发者提供了多种工具,让代码更加优雅、简洁和高效!🎉


7. 总结:装饰器,让代码更高效

通过本文的讲解,相信你已经对装饰器有了一个更深入的了解!装饰器不仅能让我们的代码更简洁、易于维护,还能提高开发效率。它在日志记录、性能测试、结果缓存、权限验证和参数验证等方面都能发挥强大的作用。

怎么样,花姐今天讲解的装饰器是不是让你眼前一亮呢?🎉 希望大家都能掌握它,用它来写出更简洁、优雅的代码!