Python的装饰器还是不会?来看看这篇文章(建议收藏)

点赞、收藏、加关注,下次找我不迷路

哈喽,各位 Python 爱好者们!今天咱们要一起来攻克 Python 里一个超实用却又让不少新手犯迷糊的知识点 —— 装饰器。别担心,全程用大白话,搭配例子,还有口诀和记忆诀窍,保准让你轻松弄懂,以后遇到装饰器再也不头疼!


一、啥是装饰器?先从生活例子说起

咱先不聊代码,聊聊生活里的事儿。比如说,你有一件普通的 T 恤(这就好比咱们写的一个普通函数),夏天穿挺舒服,但要是去参加个稍微正式点的场合,就显得有点随意了。这时候咋办呢?你可以给它套上一件帅气的外套(这外套就是装饰器啦),瞬间就变得更得体、更实用了。而且重点是,你的 T 恤本身没被破坏,还是原来的那件 T 恤,只是多了外套的加持。

回到 Python 里,装饰器的作用其实差不多。它就是一个函数(或者说一个工具),可以给其他函数添加额外的功能,比如日志记录、权限检查、性能统计等等。而且关键的是,原来的函数不需要做任何修改,就像 T 恤不用剪剪裁裁,直接套上外套就行。这就叫 "开放封闭原则",对函数功能开放扩展,对函数本身封闭修改,是不是很巧妙?


二、先看一个简单例子,感受装饰器的魅力

咱先写一个简单的函数,比如计算两个数的和:

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


现在,我们想在调用这个函数前后,打印一些日志,记录函数的调用情况。如果不用装饰器,咱们可能会这么做:

def add(a, b):
    print(f"开始调用add函数,参数是{a}和{b}")
    result = a + b
    print(f"add函数调用结束,结果是{result}")
    return result


这样确实能实现功能,但问题来了,如果有很多个函数都需要添加日志功能,难道每个函数都要这么改吗?那得多麻烦啊,而且违反了 "开放封闭原则"。这时候装饰器就派上用场了!

咱们来定义一个装饰器函数,专门给其他函数添加日志功能:

def log_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"开始调用{func.__name__}函数,参数是{args}和{kwargs}")
        result = func(*args, **kwargs)
        print(f"{func.__name__}函数调用结束,结果是{result}")
        return result
    return wrapper


然后,我们就可以用这个装饰器来装饰我们的 add 函数啦:

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

这样,当我们调用 add (3, 5) 时,就会自动打印日志,而且 add 函数本身没有任何修改。是不是很神奇?这就是装饰器的基本用法。


三、装饰器的定义和基本结构

(一)装饰器的定义

装饰器本质上就是一个函数,它接收一个函数作为参数,返回一个新的函数。这个新的函数通常会在原函数的基础上添加一些额外的功能。

(二)基本结构

装饰器一般有三层结构:

层次

名称

作用

第一层

装饰器函数(外函数)

接收被装饰的函数作为参数

第二层

包装函数(内函数)

定义新的功能逻辑,包裹原函数

第三层

原函数

被装饰的函数,保持不变

就像咱们上面的 log_decorator 就是外函数,接收 func(原函数)作为参数;wrapper 是内函数,在里面添加了日志功能,然后调用原函数 func,并返回结果。


四、装饰器离不开闭包,啥是闭包?

在讲装饰器的时候,经常会提到闭包,那闭包到底是啥呢?其实闭包就是一个内部函数引用了外部函数的变量,并且外部函数返回了这个内部函数,这样就在内部函数和外部函数的变量之间形成了一个封闭的环境,这个内部函数就被称为闭包。

举个简单的例子:

def outer():
    x = 10
    def inner():
        print(x)
    return inner


这里的 inner 函数就是一个闭包,它引用了 outer 函数中的变量 x,当我们调用 outer () 返回 inner 函数后,即使 outer 函数已经执行完毕,x 的值仍然被保留在 inner 函数中,这就是闭包的作用。装饰器正是利用了闭包的这个特性,才能在不修改原函数的情况下,为其添加功能。


五、装饰器的常见应用场景

装饰器在实际开发中应用非常广泛,下面咱们列举几个常见的场景:

(一)日志记录

就像咱们上面的例子,给函数添加日志,记录函数的调用时间、参数、结果等信息,方便调试和监控。

(二)权限校验

比如在一个网站中,有些函数(比如修改用户信息的函数)需要用户登录后才能调用,我们可以用装饰器来检查用户是否登录,如果没登录就提示用户先登录。(Python的Web开发框架常用到这种功能

def login_required(func):
    def wrapper(*args, **kwargs):
        if not is_logged_in():  # 假设is_logged_in()是检查用户是否登录的函数
            print("请先登录!")
            return
        return func(*args, **kwargs)
    return wrapper


(三)性能测试

我们可以用装饰器来计算函数的执行时间,看看函数运行得快不快,有没有优化的空间。

import time

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

(四)缓存结果

对于一些计算耗时的函数,如果我们希望多次调用时不需要重复计算,可以用装饰器来缓存函数的结果。Python 中有一个 lru_cache 装饰器就是专门做这个的。


六、带参数的装饰器,稍微复杂点,但也不难

前面咱们的装饰器都是不带参数的,那如果装饰器需要接收参数呢?比如,我们想让日志装饰器可以自定义日志的前缀,这时候就需要带参数的装饰器了。

带参数的装饰器其实就是在原来的装饰器外面再套一层函数,用来接收参数。咱们来看例子:

def log_decorator_with_param(prefix):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"{prefix}开始调用{func.__name__}函数,参数是{args}和{kwargs}")
            result = func(*args, **kwargs)
            print(f"{prefix}{func.__name__}函数调用结束,结果是{result}")
            return result
        return wrapper
    return decorator


使用的时候就需要多一层调用,比如:

@log_decorator_with_param("【重要日志】")
def add(a, b):
    return a + b

这样,日志的前缀就变成了 "【重要日志】",可以根据不同的需求传入不同的参数,是不是很灵活?


七、装饰器的语法糖 @,到底是啥?

咱们前面一直在用 @符号来应用装饰器,比如 @log_decorator,这个 @符号其实就是 Python 提供的一个语法糖,让我们使用装饰器更方便。它的本质就是把被装饰的函数作为参数传递给装饰器函数,并将返回的新函数赋值给原来的函数名。

比如,@log_decorator 等价于:

add = log_decorator(add)

有了这个语法糖,代码看起来更简洁,可读性更好。


八、记忆诀窍

(一)注意事项

  1. 装饰器的顺序很重要,如果有多个装饰器装饰同一个函数,装饰器的执行顺序是从下往上的,而调用顺序是从上往下的。
  1. 装饰器会改变原函数的元信息(比如函数名、文档字符串等),如果需要保留原函数的元信息,可以使用 functools.wraps 装饰器,比如在包装函数上加上 @functools.wraps (func)。

(二)记忆诀窍

咱们来编个口诀,帮助大家记忆装饰器的核心要点:

" 装饰器,像外套,函数穿上功能妙;

闭包里面套一套,原函不改新效到;

@符号,真方便,语法糖儿来相伴;

参数若要传进去,外层再把函数建;

日志权限和性能,各种场景都能用;

新手别怕慢慢学,多练多写就会懂!"


装饰器是 Python 中非常强大和实用的一个特性,学会使用装饰器可以让我们的代码更加简洁、优雅,提高代码的复用性。新手朋友们刚开始可能会觉得有点难,没关系,多动手写几个例子,结合咱们的口诀和记忆诀窍,慢慢就会掌握啦。希望这篇文章能让你对装饰器有一个清晰的认识,以后在编程中能灵活运用装饰器来提升自己的代码质量。

如果大家还有什么疑问,或者想了解更多 Python 编程技巧,欢迎在评论区留言,咱们一起交流学习!

相关文章

干货 | 一文讲清楚Python之装饰器

概念介绍装饰器(decorator),又称“装饰函数”,即一种返回值也是函数的函数,可以称之为“函数的函数”。其目的是在不对现有函数进行修改的情况下,实现额外的功能。最基本的理念来自于一种被称为“装饰...

Python修饰器,终极解释来了!

修饰器在本质上就是一个函数,可接收其他函数作为参数。接下来,让我们看看如何自定义一个修饰器#这个修饰器可在被修饰函数之前被执行 def xiushi1(func): print("定义...

Python 装饰器:如何改进代码的功能和可读性

什么是装饰器?用装饰器来扩展另一个函数的功能。通过使用装饰器,可以添加其他功能。为了定义装饰器,需要定义一个函数,该函数具有一个调用 wrapper 的函数和一个调用 func, 的参数,该参数是我们...

python装饰器详解

装饰器储备知识1:*args和**kwargs储备知识2:名称空间与作用域储备知识3:函数对象储备知识4:函数的嵌套定义储备知识 5:闭包函数喜欢编程的小伙伴可以加小编的Q群1026782549,进群...

Python中自带的三个装饰器

说到装饰器,就不得不说python自带的三个装饰器:1、@property 将某函数,做为属性使用@property 修饰,就是将方法,变成一个属性来使用。2、@classmethod 修饰类的方式带...