带你超深度剖析Python装饰器函数

简介: 带你超深度剖析Python装饰器函数

目录

🎑前言:

🎑一、装饰器 —— 形成过程

🎑二、装饰器 —— 初识语法糖

🎑三、装饰器 ——本质与功能

🎑四、装饰器 —— 装饰带参数,返回值的装饰器

💥1、装饰带一个参数的函数

💥2、装饰多个带有不同参数但无返回值的函数

💥3、装饰多个带有不同参数且有返回值的函数

💥4、多个装饰器装饰同一个函数

🎑五、装饰器 ——  装饰器进阶与优化

💥1、带参数的装饰器

💥2、防止函数必要信息失效

🎑六、装饰器 —— 装饰原则

💥1、开放封闭原则

🎑小结:

💥1、装饰器的固定格式(模板))



🎑前言:

假如我写了一个函数 f

def f():
    print('hello')

之后我想知道这段函数执行所要的时间,这好办,我只要将代码改为如下就行

import time
def f():
    start = time.time()   #获取程序执行开始的时间
    print('hello')
    end = time.time()     #获取程序执行结束的时间
    print(end - start)    #得出函数f执行所要时间

f()

但之后我有写了无数个函数f2,f3……fn,我想知道每个函数执行所需要的时间,那么如果都像上面一样改,岂不是很闹心?还是不行,因为这样实在是太麻烦了。那怎么办呢?于是灵机一动,写了一个timer函数。。。

import time
def timer(func):
    start = time.time()
    func()
    print(time.time() - start)

def f():
    print('hello')


def f2():
    print('xorld')

timer(f)
timer(f2)

这样看起来是不是简单多啦?不管我们写了多少个函数都可以调用这个计时函数来计算函数的执行时间

但是如果我只想用原来的方式f1(),f2(),fn()调用了这个函数,函数在原本执行输出的结果不变的前提下还可以增加计算时间的功能,而不是调用timer(f),timer(f2)才能计算时间,这该怎么办呢?

看了下面的装饰器函数你就会知道如何解决这个问题



🎑一、装饰器 —— 形成过程

以下就是解决上面问题的代码的简单版:

import time

def f():
    print('hello')

def timer(func):
    def inner():
        start = time.time()
        func()
        print(time.time() - start)
    return inner

f = timer(f)
f()

还是这句话我只想用原来的方式f1(),f2(),fn()调用了这个函数,函数在原本执行输出的结果不变的前提下还可以增加计算时间的功能,但我还是要在函数 f 执行前写 f = timer(f)在这一串代码,是不是觉得碍眼?python的开发者也觉得碍眼,所以python的开发者就为我们提供了一句语法糖来解决这个问题!



🎑二、装饰器 —— 初识语法糖

用@timmer代替f = timer(f),这就是一句语法糖。

import time
def timer(func):
    def inner():
        start = time.time()
        func()
        print(time.time() - start)
    return inner

@timer   #==> 写着这句话就相当于执行了f = timer(f)
def f():
    print('hello')


f()



🎑三、装饰器 ——本质与功能

1、本质

        装饰器的本质就是一个闭包函数

2、功能

        在不修改原函数及其调用方式的情况下对原函数功能进行扩展



🎑四、装饰器 —— 装饰带参数,返回值的装饰器

  • 💥1、装饰带一个参数的函数

刚才我们写的装饰器都是装饰不带参数的函数,现在要装饰一个带参数的函数怎么办呢?

import time
def timer(func):
    def inner(a):
        start = time.time()
        func(a)
        print(time.time() - start)
    return inner

@timer
def f(a):
    print(a)

f('hello')

  • 💥2、装饰多个带有不同参数但无返回值的函数

其实装饰带参的函数并不是什么难事,但假如你有两个函数,需要传递的参数不一样呢,比如  函数func1有两个参数func1(a ,b),函数func 2只有一个参数func2(a), 且它们都想用这个装饰器装饰,做到计算函数执行时间?这怎么办呢?那就用下面代码。

import time
def timer(func):
    def inner(*args,**kwargs):
        start = time.time()
        re = func(*args,**kwargs)
        print(time.time() - start)
        return re
    return inner

@timer   #==> func1 = timer(func1)
def func1(a,b):
    print('in func1')

@timer   #==> func2 = timer(func2)
def func2(a):
    print('in func2 and get a:%s'%(a))
    return 'fun2 over'

func1('aaaaaa','bbbbbb')
print(func2('aaaaaa'))

输出结果:
in func1
0.0
in func2 and get a:aaaaaa
0.0
fun2 over

  • 💥3、装饰多个带有不同参数且有返回值的函数

现在参数的问题已经完美的解决了,可是如果你的函数是有返回值的呢?用上面的代码你就拿不到返回值了那究竟要如何解决这个问题呢?那就看下面的代码吧!

import time
def timer(func):
    def inner(*args,**kwargs):
        start = time.time()
        re = func(*args,**kwargs)
        print(time.time() - start)
        return re
    return inner

@timer   #==> func2 = timer(func2)
def func2(a):
    print('in func2 and get a:%s'%(a))
    return 'fun2 over'

func2('aaaaaa')
print(func2('aaaaaa'))

输出结果:
in func2 and get a:aaaaaa
0.0
in func2 and get a:aaaaaa
0.0
fun2 over

  • 💥4、多个装饰器装饰同一个函数

有些时候,我们也会用到多个装饰器装饰同一个函数的情况。

ef wrapper1(func):   #func ----- f
    def inner1():
        print('wrapper1 ,before func')
        func()
        print('wrapper1 ,after func')
    return inner1

def wrapper2(func):
    def inner2():
        print('wrapper2 ,before func')
        func()
        print('wrapper2 ,after func')
    return inner2

@wrapper2       #f = wrapper2(f) ----->> wrapper2(inner1)  == inner2
@wrapper1       #f = wrapper1(f) = inner
def f():
    print('in f')
f()    #===>>inner2
#多个装饰器装饰同一个函数

输出结果:
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func



🎑五、装饰器 ——  装饰器进阶与优化

  • 💥1、带参数的装饰器

上面那个装饰器已经非常beautiful了,但是还有一个问题,如果我给代码中无数个函数都加了@timer这个语法糖,如果之后我又不想用它了那岂不是又要每个去将它注释,没日没夜忙活3天?岂不是特别麻烦,为了使装饰器不用时能够更好的回收而不是一个一个去注释或者删除 我们引入带参数的装饰器概念

'''
为了使装饰器不用时能够更好的回收而不是一个一个去注释或者删除
我们引入带参数的装饰器概念
'''

import time
'''FLAGE的目的是用它控制装饰器的开关,
那么当我们不用的时候就不要一个一个去注释只需将True改为False就行'''

FLAGE = True
def timmer_out(flag):
    def timmer(func):
        def inner(*args,**kwargs):
            if flag:
                start = time.time()
                ret = func(*args,**kwargs)
                end = time.time()
                print(end - start)
                return ret
            else:
                ret = func(*args, **kwargs)
                return ret
        return inner
    return timmer
@timmer_out(FLAGE)

#timmer_out(FLAGE)
# 也相当于执行  timmer_out(FLAGE)--->>返回timmer———————>>@timmer(wahaha = timmer(wahaha))
def wahaha():
    time.sleep(0.1)         #不休息的话函数执行的太快难以计算时间
    print('wahahahahahaha')

wahaha()

@timmer_out(FLAGE)
def erguotou():
    time.sleep(0.1)         #不休息的话函数执行的太快难以计算时间
    print('erguotoutoutou')

erguotou()

输出结果:
wahahahahahaha
0.10152268409729004
erguotoutoutou
0.10795140266418457

  • 💥2、防止函数必要信息失效

'''
print(wahaha.__name__)      #查看字符串格式的函数名
print(wahaha.__doc__)       #查看一个函数的注释
'''
#下面用__name__查看holiday的函数名

from functools import wraps
def wrapper(func):
    @wraps(func)            #加在最内层函数正上方
    def inner(*args,**kwargs):
        print('在被装饰的函数执行之前做的事')
        ret = func(*args,**kwargs)
        print('在被装饰的函数执行之后做的事')
        return ret
    return inner

@wrapper        #holiday = wrapper(holiday)
def holiday(day):
    '''
    这是一个放假通知
    :param day:
    :return:
    '''
    print('全体放假%s天'%day)
    return '好开心'

print(holiday.__name__)
print(holiday.__doc__)
'''
结果是inner和None 但我们想要的是打印holiday的字符串格式的函数名和函数的注释这时该怎么办?
解决方法就是  from functools import wraps
使用语法是@wraps(被装饰的函数名)
'''

输出结果:
holiday

    这是一个放假通知
    :param day:
    :return:



🎑六、装饰器 —— 装饰原则

  • 💥1、开放封闭原则

1.对原函数的功能扩展是开放的

        为什么要对功能扩展开放呢?

    对于任何一个程序来说,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许后来扩展、添加新功能。

2.对修改是封闭的

      为什么要对修改封闭呢?

                就像我们刚刚提到的,因为我们写的一个函数,很有可能在其他地方已经被导入使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经正在使用该函数的代码。

装饰器就完美遵循了这个开放封闭原则。这就是学装饰器的初衷



🎑小结:

  • 💥1、装饰器的固定格式(模板)

#格式一

def timer(func):
    def inner(*args,**kwargs):
        '''执行函数之前要做的'''
        re = func(*args,**kwargs)
        '''执行函数之后要做的'''
        return re
    return inner

#格式二

from functools import wraps

def deco(func):
    @wraps(func) #加在最内层函数正上方
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper

相关文章
|
6天前
|
Python
【python从入门到精通】-- 第五战:函数大总结
【python从入门到精通】-- 第五战:函数大总结
29 0
|
3天前
|
Python
Python之函数详解
【10月更文挑战第12天】
Python之函数详解
|
4天前
|
存储 数据安全/隐私保护 索引
|
6天前
|
Python
探索Python中的装饰器:从基础到高级
【10月更文挑战第11天】 在这篇文章中,我们将深入探讨Python装饰器的强大功能和灵活应用。装饰器是Python中一个非常有趣的特性,它允许我们修改或增强函数的行为,而无需直接修改函数本身的代码。通过使用装饰器,我们可以实现横切关注点(AOP)的编程范式,提高代码的可重用性和模块化。本文将介绍装饰器的基本概念、使用方法以及如何创建自定义装饰器。同时,我们还将探讨装饰器的一些高级用法,如带参数的装饰器、多层装饰器和偏函数装饰器等。
15 5
|
4天前
|
存储 程序员 Python
了解Python中的装饰器 | python小知识
装饰器是Python中一个非常强大且灵活的特性,它允许程序员在不改变函数本身的情况下扩展或修改函数的行为。本文将带你从零开始,了解装饰器的工作原理,常见的基本操作,并深入介绍`@dataclass`和`@property`装饰器的用法。 【10月更文挑战第10天】
16 2
|
4天前
|
设计模式 数据安全/隐私保护 开发者
Python中的装饰器:从基础到高级应用
本文将深入探讨Python中一个极其强大且灵活的特性——装饰器。装饰器本质上是一个函数,它允许我们对另一个函数或类进行扩展,而无需永久性地修改它们。这一特性使得装饰器成为实现横切关注点(如日志记录、访问控制等)的理想工具。我们将从装饰器的基本概念入手,逐步讲解其工作原理,并通过一系列示例展示如何在实际项目中巧妙利用装饰器来提升代码的可维护性和可读性。最后,我们还将探索一些高级装饰器技巧,帮助你在编写Python程序时更加游刃有余。
|
4天前
|
缓存 程序员 开发者
探索Python中的装饰器:一种优雅的代码增强技巧
【10月更文挑战第13天】 在本文中,我们将深入探讨Python中的装饰器,这是一种强大的工具,它允许程序员以简洁而高效的方式扩展或修改函数和类的行为。通过具体示例,我们将展示如何利用装饰器来优化代码结构,提高开发效率,并实现如日志记录、性能计时等常见功能。本文旨在为读者提供一个关于Python装饰器的全面理解,从而能够在他们的项目中灵活运用这一技术。
15 1
|
5天前
|
索引 Python
Python中的其他内置函数有哪些
【10月更文挑战第12天】Python中的其他内置函数有哪些
7 1
|
6天前
|
缓存 测试技术 开发者
探索Python中的装饰器:从基础到高级应用
本文深入探讨了Python中装饰器的概念、作用及其在实际编程中的应用。装饰器是一种特殊类型的函数,它允许我们在不修改现有代码的情况下,增加或修改类或函数的行为。我们将从装饰器的基本定义开始,逐步讲解其工作原理,并通过实例展示如何创建和使用基本的装饰器。进一步地,本文还将介绍一些高级装饰器技术,包括带参数的装饰器、使用functools.wraps进行签名保全、以及如何在类中使用装饰器。最后,我们将探讨装饰器的实际应用案例,帮助读者更好地理解和运用这一强大的Python特性。
|
8天前
|
设计模式 开发者 Python
Python中的装饰器:简化代码与增强功能
【10月更文挑战第9天】在编程的世界里,效率和可读性是衡量代码质量的两大关键指标。Python语言以其简洁明了的语法赢得了无数开发者的青睐,而装饰器则是其独特魅力之一。本文将深入探讨装饰器的工作原理、使用方法以及如何通过自定义装饰器来提升代码的重用性和可维护性,让读者能够更加高效地编写出既优雅又功能强大的代码。