Maison  >  Article  >  développement back-end  >  Un examen plus approfondi des fonctions du décorateur Python

Un examen plus approfondi des fonctions du décorateur Python

WBOY
WBOYavant
2022-06-24 12:50:421974parcourir

Cet article vous apporte des connaissances pertinentes sur python, qui organise principalement les problèmes liés aux fonctions des décorateurs, y compris le processus de formation, l'essence et la fonction, l'avancement et l'optimisation des décorateurs, etc. Ce qui suit est Jetons un coup d'œil, j'espère que cela aidera tout le monde .

Un examen plus approfondi des fonctions du décorateur Python

Apprentissage recommandé : python

Si j'écris une fonction f

def f():
    print('hello')

et que je veux connaître le temps qu'il faut pour exécuter cette fonction, c'est facile à faire, il me suffit de changer le code en ce qui suit

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

f()

Mais ensuite j'ai écrit d'innombrables fonctions f2, f3...fn. Je voulais connaître le temps nécessaire pour exécuter chaque fonction. Ne serait-ce pas très ennuyeux si je les modifiais toutes comme ci-dessus ? Ce n’est toujours pas possible, car cela poserait trop de problèmes. Ce qu'il faut faire? J'ai donc eu une idée et j'ai écrit une fonction de minuterie. . .

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)

Cela n'a-t-il pas l'air beaucoup plus simple ? Peu importe le nombre de fonctions que nous écrivons, nous pouvons appeler cette fonction de timing pour calculer le temps d'exécution de la fonction

Mais si je veux juste appeler cette fonction de la manière originale f1(), f2(), fn(), le La fonction sera exécutée de la manière originale. En partant du principe que le résultat de sortie reste inchangé, la fonction de calcul du temps peut être ajoutée. Au lieu d'appeler timer(f), timer(f2) peut calculer le temps.

Vous saurez comment résoudre ce problème après avoir lu la fonction décorateur ci-dessous



1 Décorateur - Processus de formation

Ce qui suit est une version simple du code pour résoudre le problème ci-dessus :

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()

C'est ce que j'ai. J'ai dit que je voulais juste appeler cette fonction de la manière originale f1(), f2(), fn(). La fonction peut également augmenter le temps de calcul sans modifier le résultat de l'exécution d'origine, mais je dois quand même l'exécuter dans la fonction f. Trouvez-vous ennuyeux d'écrire f = timer(f) dans cette chaîne de code ? Les développeurs Python trouvent également cela ennuyeux, alors les développeurs Python nous fournissent un sucre syntaxique pour résoudre ce problème !



2. Décorateur - Première introduction au sucre syntaxique

Remplacez f = timer(f) par @timmer C'est une phrase de sucre syntaxique.

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()


3. Décorateur - Essence et Fonction

1. Essence

L'essence du décorateur est une fonction de fermeture

2. Fonction

Sans modifier la fonction d'origine et sa méthode d'appel Étendre la fonction de la fonction originale dans certaines circonstances

4. Décorateurs - décorateurs avec paramètres et valeurs de retour

  • 1. Décorer les fonctions avec un paramètre

Les décorateurs que nous venons d'écrire décorent tous une fonction sans paramètres. Que dois-je faire maintenant si je veux décorer une fonction avec des paramètres ?

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. Décorez plusieurs fonctions avec des paramètres différents mais aucune valeur de retour

En fait, il n'est pas difficile de décorer des fonctions avec des paramètres, mais si vous avez deux fonctions, les paramètres qui doivent être transmis sont différents ,Par exemple, la fonction func1 a deux paramètres func1(a,b), la fonction func2 n'a qu'un seul paramètre func2(a), et elles veulent toutes les deux être décorées avec ce décorateur pour calculer le temps d'exécution de la fonction ? Ce qu'il faut faire? Utilisez ensuite le code suivant.

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. Décorez plusieurs fonctions avec différents paramètres et valeurs de retour

Maintenant, le problème des paramètres a été parfaitement résolu, mais que se passe-t-il si votre fonction a une valeur de retour ? Vous ne pouvez pas obtenir la valeur de retour en utilisant le code ci-dessus. Alors, comment résoudre ce problème ? Alors regardez le code ci-dessous !

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. Plusieurs décorateurs décorent la même fonction

Parfois, nous utiliserons également plusieurs décorateurs pour décorer la même fonction.

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


5. Décorateur - Avancement et optimisation du décorateur

  • 1. Décorateur avec paramètres

Le décorateur ci-dessus est déjà très beau, mais il y a un autre problème si je donne d'innombrables fonctions dans le code. sucre syntaxique de @timer ajouté. Si je ne veux plus l'utiliser, ne devrais-je pas le commenter à nouveau et travailler jour et nuit pendant 3 jours ? N'est-ce pas particulièrement gênant ? Afin de mieux recycler les décorateurs lorsqu'ils ne sont pas utilisés au lieu de les annoter ou de les supprimer un par un, nous introduisons le concept de décorateurs avec paramètres

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

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

推荐学习:python

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer