Créons un "décorateur" Python. Créez un cadre de décorateur générique.
Faire. Tout d'abord, nous présenterons le cadre du décorateur (objectif) et y travaillerons.
Cet article est écrit pour être lu (vers le but en répétant des essais et des erreurs) et est une longue phrase, donc je vais d'abord décrire uniquement comment écrire le décorateur (cadre, cadre). [La version complète est répertoriée en dernier](# decorator_frameworkpy-full-version) y compris le test. Si vous êtes occupé, jetez-y un œil lorsque vous en aurez le temps.
decorator_framework.py
from functools import wraps
def my_decorator( *args, **kwargs ):
#Pour les décorateurs qui ne prennent clairement aucun argument, partez d'ici
# _my_Vous pouvez renommer le décorateur et le définir globalement.
def _my_decorator( func ):
# _my_decorator_body()S'il y a un traitement nécessaire avant de définir, écrivez-le ici
print( "_my_decorator_body()S'il y a un traitement nécessaire avant de définir, écrivez-le ici" )
@wraps(func)
def _my_decorator_body( *body_args, **body_kwargs ):
#Le prétraitement se fait ici
print( "Le prétraitement se fait ici", args, kwargs, body_args, body_kwargs )
try:
#Exécution du corps décoré
ret = func( *body_args, **body_kwargs )
except:
raise
#Le post-traitement se fait ici
print( "Le post-traitement se fait ici", args, kwargs, body_args, body_kwargs )
return ret
#Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#2
print( "Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#2" )
return _my_decorator_body
#C'est la fin du décorateur qui ne prend clairement aucun argument
#Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#1
print( "Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#1" )
if len(args) == 1 and callable( args[0] ):
#Si le décorateur est appelé sans argument, gérez-le ici
print( "No arguments" )
return _my_decorator( args[0] )
else:
#Si le décorateur est appelé avec un argument, traitez-le ici
print( "There are some arguments:", args )
return _my_decorator
Encore plus simple.
decorator_framework.py Version simple
from functools import wraps
def my_decorator( *args, **kwargs ):
def _my_decorator( func ):
# _my_decorator_body()S'il y a un traitement nécessaire avant de définir, écrivez-le ici
@wraps(func)
def _my_decorator_body( *body_args, **body_kwargs ):
#Le prétraitement se fait ici
try:
#Exécution du corps décoré
ret = func( *body_args, **body_kwargs )
except:
raise
#Le post-traitement se fait ici
return ret
#Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#2
return _my_decorator_body
#Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#1
if len(args) == 1 and callable( args[0] ):
#Si le décorateur est appelé sans argument, gérez-le ici
return _my_decorator( args[0] )
else:
#Si le décorateur est appelé avec un argument, traitez-le ici
return _my_decorator
Commençons maintenant par créer un décorateur.
Le glossaire décrit le décorateur comme suit:
decorator dt> (decorator) Une fonction qui renvoie une autre fonction, généralement
@wrapper span> code>. Un cas d'utilisation courant pour les décorateurs est
< span class = "pre"> classmethod () span> code>
etstaticmethod () span> code>
. p>La grammaire du décorateur est le sucre de syntaxe. Les deux définitions de fonction suivantes sont sémantiquement identiques: p>
def f(...): ... f = staticmethod(f)@staticmethod def f(...): ...
Le même concept existe dans les classes, mais il est rarement utilisé. Pour plus d'informations sur les décorateurs, consultez définition de fonction span> et définition de classe span> . p>
(Extrait de decorator-Glossary - Python 3.8.1 Documentation)
Dans l'exemple cité ci-dessus, «@ staticmethod» est le décorateur.
De plus, la description de l'élément de définition de fonction est la suivante.
Une ou plusieurs définitions de fonction decorator span> . Lorsque vous définissez une fonction, l'expression de décorateur est évaluée dans la portée qui contient la définition de fonction. Le résultat doit être un objet appelable qui prend un objet fonction comme seul argument. Au lieu d'un objet fonction, la valeur renvoyée est liée au nom de la fonction. Plusieurs décorateurs sont imbriqués et appliqués. Par exemple, le code suivant: p>
@f1(arg) @f2 def func(): passéquivaut à peu près à p>
def func(): pass func = f1(arg)(f2(func))Cependant, dans l'ancien code, il n'est pas possible de lier temporairement la fonction d'origine au nom
func span> code>. Sauf là où il n'y en a pas. p>
(Cité de définition de fonction --8 instruction composée - documentation Python 3.8.1)
La grammaire du décorateur est le sucre de syntaxe
Certaines personnes peuvent ne pas être familières avec le mot «sucre syntaxique». En termes simples, une description complexe (obscurcie) qui peut être remplacée par une simple notation, ou vice versa, est appelée «sucre de syntaxe».
En d'autres termes, le décorateur est un remplaçant réinscriptible, similaire à ce que les autres langages appellent "macro". (Cependant, la méthode de remplacement a été décidée)
Prenons l'exemple de la citation du glossaire.
Exemple 1
def f(...):
...
f = staticmethod(f)
Avec cet exemple 1
Exemple 2
@staticmethod
def f(...):
...
Il dit que cet exemple 2 est le même. Que voulez-vous dire.
Ce que fait l'exemple 1 est
staticmethod ()
avec la fonction définie f ()
(objet fonction) comme argument.staticmethod ()
à la fonction f.
Cela signifie que.L'exemple 2 en est une version simplifiée, et «@ staticmethod» est appelé un «décorateur».
Après cela, lorsque la fonction f ()
est réellement appelée, la fonction redéfinie est appelée comme valeur de retour de staticmethod ()
.
Puisque @ staticmethod
est en fait un décorateur standard, ce n'est pas pratique comme exemple, nous allons donc procéder comme @ my_decorator
ci-dessous.
Il y a quelques choses que vous pouvez dire à partir de l'exemple ci-dessus seul.
my_decorator
est une fonction. (Pour être exact, "objet appelable")my_decorator ()
prend un objet fonction comme argument.my_decorator ()
renvoie une fonction.à propos de ça.
Le code ci-dessous montre ce que nous avons trouvé ci-dessus.
my_decorator Premier pas
def my_decorator(func):
return func
Il ne fait rien et renvoie la fonction reçue en argument. Les appels qui n'utilisent pas «@» ressembleront à l'exemple 001.
sample 001
>>> def my_decorator_001(func):
... return func
...
>>> def base_func():
... print( "in base_func()" )
...
>>> base_func = my_decorator_001( base_func )
>>> base_func()
in base_func()
>>>
Si vous utilisez @
comme décorateur, il ressemblera à l'exemple 002.
sample 002
>>> def my_decorator_001(func):
... return func
...
>>> @my_decorator_001
... def base_func():
... print( "in base_func()" )
...
>>> base_func()
in base_func()
>>>
Vous ne savez pas ce qui se passe avec ça. Cependant, il est à noter que lorsque vous entrez @ my_decorator_001
, il attend une entrée continue.
Ajoutons un affichage dans my_decorator ()
.
sample 003
>>> def my_decorator_002(func):
... print( "in my_decorator_002()" )
... return func
...
>>> def base_func():
... print( "in base_func()" )
...
>>> base_func = my_decorator_002(base_func)
in my_decorator_002()
>>> base_func()
in base_func()
>>>
Ensuite, utilisons un décorateur.
sample 004
>>> def my_decorator_002(func):
... print( "in my_decorator_002()" )
... return func
...
>>> @my_decorator_002
... def base_func():
... print( "in base_func()" )
...
in my_decorator_002()
>>> base_func()
in base_func()
>>>
La fonction my_decorator_002 ()
n'est pas exécutée lorsque @ my_decorator_002
est écrit, et ʻin my_decorator ()est affiché lorsque la définition de
def base_func ():est complétée en dessous. Le point à noter est que c'est la même chose qui se produit lorsque vous exécutez
base_func = my_decorator_002 (base_func)` dans l'exemple 003.
Auparavant, la fonction donnée à l'argument du décorateur était retournée telle quelle avec return. Quelle est la fonction de retour?
Essayons d'abord d'utiliser une fonction globale pour voir ce qui se passe lorsque nous renvoyons une autre fonction.
sample 005
>>> def global_func():
... print( "in global_func()" )
...
>>> def my_decorator_005(func):
... print( "in my_decorator_005()" )
... return global_func
...
>>> @my_decorator_005
... def base_func():
... print( "in base_func()" )
...
in my_decorator_005()
>>> base_func()
in global_func()
>>>
En exécutant base_func ()
, global_func ()
a été appelé sans aucun problème.
Mais, bien sûr, l'original base_func ()
ne fonctionne pas.
Pensez à exécuter la fonction d'origine lors de l'exécution de la nouvelle fonction.
La fonction d'origine est transmise en tant qu'argument de la fonction de décorateur, donc son appel exécutera la fonction d'origine.
sample 006
>>> def global_func():
... print( "in global_func()" )
...
>>> def my_decorator_006(func):
... print( "in my_decorator_006()" )
... func() #Appel à la fonction d'origine
... return global_func
...
>>> @my_decorator_006
... def base_func():
... print( "in base_func()" )
...
in my_decorator_006()
in base_func() #J'ai appelé la fonction d'origine ici
>>> base_func() #* Je souhaite appeler la fonction d'origine ici
in global_func()
>>>
La fonction d'origine base_func ()
a été appelée lorsque @ my_decorator_006
a été spécifié.
Le moment où vous voulez appeler base_func ()
est lorsque vous appelez base_func ()
avec "*".
En d'autres termes, nous voulons appeler base_func ()
dans global_func ()
.
Modifions-le un peu pour qu'il puisse être appelé dans global_func ()
.
sample 007
>>> #Conservez la fonction d'origine
... original_func = None
>>>
>>> def global_func():
... global original_func
... print( "in global_func()" )
... original_func() #La fonction d'origine doit être appelée
...
>>> def my_decorator_007(func):
... global original_func
... print( "in my_decorator_007()" )
... original_func = func #La fonction passée à l'argument est d'origine globale_Pour func
... return global_func
...
>>> @my_decorator_007
... def base_func():
... print( "in base_func()" )
...
in my_decorator_007()
>>> base_func() #* Je souhaite appeler la fonction d'origine ici
in global_func()
in base_func()
>>>
C'est un peu déroutant, mais j'ai pu l'appeler à partir de global_func ()
en préparant la variable globale ʻoriginal_func et en sauvegardant le
func` passé comme argument ici.
Cependant, cela pose un problème. Lorsque j'essaie d'utiliser le décorateur pour plusieurs fonctions, cela ne fonctionne pas comme prévu.
sample 008
>>> #Conservez la fonction d'origine
... original_func = None
>>>
>>> def global_func():
... global original_func
... print( "in global_func()" )
... original_func() #La fonction d'origine doit être appelée
...
>>> def my_decorator_007(func):
... global original_func
... print( "in my_decorator_007()" )
... original_func = func #La fonction passée à l'argument est d'origine globale_Pour func
... return global_func
...
>>> @my_decorator_007
... def base_func():
... print( "in base_func()" )
...
in my_decorator_007()
>>> @my_decorator_007
... def base_func_2():
... print( "in base_func_2()" )
...
in my_decorator_007()
>>> base_func() # "in base_func()"Je veux que tu sois affiché
in global_func()
in base_func_2() ← "in base_func()"ne pas"in base_func_2()"A été affiché
>>> base_func_2() # "in base_func_2()"Je veux que tu sois affiché
in global_func()
in base_func_2()
>>>
L'appel à base_func ()
a exécuté base_func_2 ()
.
Puisqu'il n'y a qu'une seule variable globale global_func
, la dernière base_func_2
affectée est en cours d'exécution.
Parlons de "Fermeture" pour que le décorateur puisse être utilisé plusieurs fois.
Jetez un œil à sample_009.py
comme ceci:
sample_009.py
1 def outer(outer_arg):
2 def inner(inner_arg):
3 # outer_arg et intérieur_Arg de sortie
4 print( "outer_arg: " + outer_arg + ", inner_arg: " + inner_arg )
5 return inner # inner()Renvoie un objet fonction
6
7 f1 = outer("first")
8 f2 = outer("second")
9
10 f1("f1")
11 f2("f2")
Exécutez f2 = externe (" deuxième ") ʻ sur la ligne 8 après
f1 = externe (" premier ") ʻ sur la ligne 7.
Chacun se voit attribuer un objet fonction ʻinner, mais qu'en est-il de
f1 (" f1 ")à la ligne 10 et
f2 (" f2 ")` à la ligne 11.
Puisque la 8ème ligne dit f2 = external (" second ")
, à la 4ème ligne print ()
, ʻouter_arg devient
"second" `.
La sortie de «f1 (« f1 »)» sur la ligne 10 et «f2 (« f2 »)» sur la ligne 11 sont respectivement.
outer_arg: second, inner_arg: f1
outer_arg: second, inner_arg: f2
Il semble que ...
Quand je l'exécute, cela ressemble à ceci:
sample 009
>>> def outer(outer_arg):
... def inner(inner_arg):
... # outer_arg et intérieur_Arg de sortie
... print( "outer_arg:", outer_arg, ", inner_arg:", inner_arg )
... return inner # inner()Renvoie un objet fonction
...
>>> f1 = outer("first")
>>> f2 = outer("second")
>>>
>>> f1("f1")
outer_arg: first, inner_arg: f1
>>> f2("f2")
outer_arg: second, inner_arg: f2
>>>
c'est,
est" exécutée "chaque fois que la fonction ʻouter ()
est appelée.externe référencé lorsque la fonction ʻinner ()
est définie (exécutant la définition) est fixé (évalué) et maintenu par l'intérieur.
C'est pour faire le mouvement. C'est ce qu'on appelle la fermeture.
Les variables de la portée externe sont évaluées au moment de la définition, pas au moment de l'exécution.Utilisez ceci pour permettre au décorateur d'être utilisé plusieurs fois.
Dans l'exemple 009, nous avons utilisé une fonction globale, mais pour utiliser la fonction de fermeture, nous définissons la fonction à l'intérieur de la fonction décoratrice.
sample_010.py
def my_decorator_010(func):
print( "in my_decorator_010()" )
def inner():
print( "in inner() and calling", func )
func()
print( "in inner() and returnd from ", func )
print( "in my_decorator_010(), leaving ..." )
return inner
@my_decorator_010
def base_func():
print( "in base_func()" )
@my_decorator_010
def base_func_2():
print( "in base_func_2()" )
base_func() # "in base_func()"Je veux que tu sois affiché
base_func_2() # "in base_func_2()"Je veux que tu sois affiché
Faisons ceci (entrez interactivement).
sample 010
>>> def my_decorator_010(func):
... print( "in my_decorator_010()" )
... def inner():
... print( "in inner() and calling", func )
... func()
... print( "in inner() and returnd from ", func )
... print( "in my_decorator_010(), leaving ..." )
... return inner
...
>>> @my_decorator_010
... def base_func():
... print( "in base_func()" )
...
in my_decorator_010()
in my_decorator_010(), leaving ...
>>> @my_decorator_010
... def base_func_2():
... print( "in base_func_2()" )
...
in my_decorator_010()
in my_decorator_010(), leaving ...
>>> base_func() # "in base_func()"Je veux que tu sois affiché
in inner() and calling <function base_func at 0x769d1858>
in base_func()
in inner() and returnd from <function base_func at 0x769d1858>
>>> base_func_2() # "in base_func_2()"Je veux que tu sois affiché
in inner() and calling <function base_func_2 at 0x769d1930>
in base_func_2()
in inner() and returnd from <function base_func_2 at 0x769d1930>
>>>
J'ai eu le résultat attendu.
Après avoir organisé ce qui précède, nous avons constaté que le décorateur devait être codé comme suit.
sample 011
def my_decorator_011(func):
def inner():
#Écrivez le processus de pré-appel ici
func()
#Écrivez le traitement post-appel ici
return inner
Les décorateurs jusqu'à présent sont
Si vous souhaitez créer un décorateur à usage général, vous devez remplir les deux conditions ci-dessus.
Le programme prenant en compte la valeur de retour est le suivant.
sample_012.py
def my_decorator_012(func):
def inner():
#Écrivez le processus de pré-appel ici
ret = func()
#Écrivez le traitement post-appel ici
return ret
return inner
@my_decorator_012
def base_func_1():
print( "in base_func_1()" )
return 1
@my_decorator_012
def base_func_2():
print( "in base_func_2()" )
r1 = base_func_1()
print( r1 )
base_func_2()
Le résultat de l'exécution est le suivant.
sample 012
>>> def my_decorator_012(func):
... def inner():
... #Écrivez le processus de pré-appel ici
... ret = func()
... #Écrivez le traitement post-appel ici
... return ret
... return inner
...
>>> @my_decorator_012
... def base_func_1():
... print( "in base_func_1()" )
... return 1
...
>>> @my_decorator_012
... def base_func_2():
... print( "in base_func_2()" )
...
>>> r1 = base_func_1()
in base_func_1()
>>> print( r1 )
1
>>> base_func_2()
in base_func_2()
>>>
Cela fonctionnait même s'il n'y avait pas de valeur de retour (base_func_2 ()
).
Voir aussi: parameter-Glossary - Python 3.8.1 Documentation
Les arguments de fonction peuvent être reçus sous forme d'arguments formels pour lesquels le nombre d'arguments et la spécification de mot-clé ne sont pas déterminés par le paramètre de position de longueur variable et le paramètre de mot-clé de longueur variable.
Position de longueur variable em>: Étant donné n'importe quel nombre d'arguments de position (en plus de tous les arguments de position déjà reçus par d'autres arguments formels) Spécifier. Ces arguments formels doivent précéder le nom de l'argument formel, tel que args em> ci-dessous,
* span> < Il peut être défini en ajoutant / code>: p>
def func(*args, **kwargs): ...Mots-clés de longueur variable em>: étant donné n'importe quel nombre d'arguments de mot-clé (en plus de tout argument de mot-clé déjà reçu par d'autres arguments formels) Est spécifié. Ces arguments formels doivent précéder le nom de l'argument formel, tel que kwargs em> dans l'exemple ci-dessus,
** strong> Il peut être défini en ajoutant span> code>. p> li>
(Extrait du glossaire des paramètres - Documentation Python 3.8.1)
En termes simples, vous pouvez prendre un argument variable en spécifiant (* args, ** kwargs)
comme argument de fonction.
En utilisant ceci, le décorateur d'une fonction qui prend un argument peut être écrit comme suit:
sample_013.py
def my_decorator_013(func):
def inner( *args, **kwargs ):
#Écrivez le processus de pré-appel ici
ret = func( *args, **kwargs )
#Écrivez le traitement post-appel ici
return ret
return inner
@my_decorator_013
def base_func_1(arg1, arg2, arg3="arg3"):
print( "in base_func_1({}, {}, {})".format(arg1, arg2, arg3 ) )
return 1
@my_decorator_013
def base_func_2():
print( "in base_func_2()" )
r1 = base_func_1("arg1","arg2")
print( r1 )
base_func_2()
Voici le résultat de l'exécution.
sample 013
>>> def my_decorator_013(func):
... def inner( *args, **kwargs ):
... #Écrivez le processus de pré-appel ici
... ret = func( *args, **kwargs )
... #Écrivez le traitement post-appel ici
... return ret
... return inner
...
>>> @my_decorator_013
... def base_func_1(arg1, arg2, arg3="arg3"):
... print( "in base_func_1({}, {}, {})".format(arg1, arg2, arg3 ) )
... return 1
...
>>> @my_decorator_013
... def base_func_2():
... print( "in base_func_2()" )
...
>>> r1 = base_func_1("arg1","arg2")
in base_func_1(arg1, arg2, arg3)
>>> print( r1 )
1
>>> base_func_2()
in base_func_2()
>>>
Considérez également les exceptions.
sample_014.py
def my_decorator_014(func):
def inner( *args, **kwargs ):
#Écrivez le processus de pré-appel ici
try:
ret = func( *args, **kwargs )
except:
raise
#Écrivez le traitement post-appel ici
return ret
return inner
@my_decorator_014
def base_func_1(arg1, arg2, arg3="arg3"):
print( "in base_func_1({}, {}, {})".format(arg1, arg2, arg3 ) )
return 1
@my_decorator_014
def base_func_2():
print( "in base_func_2()" )
na = 1 / 0 #Une exception de division zéro se produit
r1 = base_func_1("arg1","arg2")
print( r1 )
try:
base_func_2()
except ZeroDivisionError:
print( "Zero Division Error" )
Voici le résultat de l'exécution.
sample 014
>>> def my_decorator_014(func):
... def inner( *args, **kwargs ):
... #Écrivez le processus de pré-appel ici
... try:
... ret = func( *args, **kwargs )
... except:
... raise
... #Écrivez le traitement post-appel ici
... return ret
... return inner
...
>>> @my_decorator_014
... def base_func_1(arg1, arg2, arg3="arg3"):
... print( "in base_func_1({}, {}, {})".format(arg1, arg2, arg3 ) )
... return 1
...
>>> @my_decorator_014
... def base_func_2():
... print( "in base_func_2()" )
... na = 1 / 0 #Une exception de division zéro se produit
...
>>> r1 = base_func_1("arg1","arg2")
in base_func_1(arg1, arg2, arg3)
>>> print( r1 )
1
>>>
>>> try:
... base_func_2()
... except ZeroDivisionError:
... print( "Zero Division Error" )
...
in base_func_2()
Zero Division Error
>>>
Puisqu'un décorateur est une fonction (un objet appelable), pensez à lui passer un argument.
La description de la définition de fonction citée au début comprenait également un exemple de décorateur avec des arguments.
@f1(arg)
@f2
def func(): pass
def func(): pass
func = f1(arg)(f2(func))
Puisque les décorateurs sont imbriqués, ne considérez qu'un seul décorateur pour plus de simplicité.
@my_decorator('argument')
def func(arg1, arg2, arg3):
pass
Ceci est équivalent à ci-dessous.
def func(arg1, arg2, arg3):
pass
func = my_decorator('argument')(func)
Cela signifie exécuter _my_decorator (func)
en utilisant la fonction (supposée être _my_decorator) retournée en appelant my_decorator ('argument')
.
L'imbrication devient un pas plus profond, et la fonction la plus externe (décorateur) reçoit l'argument, et le décorateur existant y est inclus.
sample015.py
def my_decorator_015( arg1 ):
def _my_decorator( func ):
def inner( *args, **kwargs ):
print( "in inner, arg1={}, func={}".format(arg1, func.__name__) )
ret = func( *args, **kwargs )
print( "in inner leaving ..." )
return ret
return inner
return _my_decorator
Voici le résultat de l'exécution.
sample 015
>>> def my_decorator_015( arg1 ):
... def _my_decorator( func ):
... def inner( *args, **kwargs ):
... print( "in inner, arg1={}, func={}".format(arg1, func.__name__) )
... ret = func( *args, **kwargs )
... print( "in inner leaving ..." )
... return ret
... return inner
... return _my_decorator
...
>>> @my_decorator_015('argument')
... def f_015( arg1, arg2, arg3 ):
... print( "in func( {}, {}, {} )".format(arg1, arg2, arg3) )
...
>>> f_015( "Argument 1", "Argument 2", "Argument 3" )
in inner, arg1=argument, func=f_015
in func(Argument 1,Argument 2,Argument 3)
in inner leaving ...
>>>
Lorsque vous appelez le décorateur avec des arguments, la partie inférieure arg1 et func sont stockées dans _my_decorator_body.
print( "in _my_decorator_body, arg1={}, func={}".format(arg1, func.__name__) )
Par conséquent, lorsque vous appelez f_015 (), les paramètres arg1 et func enregistrés sont référencés.
Le décorateur qui ne prend aucun argument est le suivant.
sample_014.py
def my_decorator_014(func):
def inner( *args, **kwargs ):
#Écrivez le processus de pré-appel ici
try:
ret = func( *args, **kwargs )
except:
raise
#Écrivez le traitement post-appel ici
return ret
return inner
Le décorateur qui passe l'argument est le suivant.
sample_016.py
def my_decorator_016( arg1 ):
def _my_decorator( func ):
def inner( *args, **kwargs ):
#Écrivez le processus de pré-appel ici
try:
ret = func( *args, **kwargs )
except:
raise
#Écrivez le traitement post-appel ici
return ret
return inner
return _my_decorator
Que se passe-t-il si vous utilisez un décorateur qui assume des arguments sans donner d'arguments?
sample_017.py
def my_decorator_017( arg1 ):
def _my_decorator( func ):
def inner( *args, **kwargs ):
#Écrivez le processus de pré-appel ici
try:
ret = func( *args, **kwargs )
except:
raise
#Écrivez le traitement post-appel ici
return ret
return inner
return _my_decorator
@my_decorator_017
def f_017( arg1, arg2, arg3 ):
print( "in {}( {}, {}, {} )".format(f_017.__name__, arg1, arg2, arg3) )
f_017( "Argument 1", "Argument 2", "Argument 3" )
Résultat d'exécution
$ python sample_017.py
Traceback (most recent call last):
File "sample_017.py", line 21, in <module>
f_017( "Argument 1", "Argument 2", "Argument 3" )
TypeError: _my_decorator() takes 1 positional argument but 3 were given
$
C'est parce que nous n'avons pas passé d'arguments à @ my_decorator_017
, donc il s'appellef_017 = my_decorator_017 (f_017)
.
Il y a deux façons d'éviter cela.
()
et de le recevoir comme argument de longueur variable.Ceci est le premier exemple.
sample_018.py
def my_decorator_018( *args, **kwargs ):
def _my_decorator( func ):
def inner( *inner_args, **inner_kwargs ):
#Écrivez le processus de pré-appel ici
try:
ret = func( *inner_args, **inner_kwargs )
except:
raise
#Écrivez le traitement post-appel ici
return ret
return inner
return _my_decorator
@my_decorator_018()
def f_018( arg1, arg2, arg3 ):
print( "in {}( {}, {}, {} )".format(f_018.__name__, arg1, arg2, arg3) )
f_018( "Argument 1", "Argument 2", "Argument 3" )
Résultat d'exécution
$ python sample_018.py
in inner(Argument 1,Argument 2,Argument 3)
La deuxième méthode utilise le premier argument du décorateur pour le jugement.
sample_019.py
def my_decorator_019( *args, **kwargs ):
def _my_decorator( func ):
def inner( *inner_args, **inner_kwargs ):
#Écrivez le processus de pré-appel ici
try:
ret = func( *inner_args, **inner_kwargs )
except:
raise
#Écrivez le traitement post-appel ici
return ret
return inner
if len(args) == 1 and callable(args[0]):
return _my_decorator( args[0] )
else:
return _my_decorator
@my_decorator_019
def f_019_1( arg1, arg2, arg3 ):
print( "in {}( {}, {}, {} )".format(f_019_1.__name__, arg1, arg2, arg3) )
@my_decorator_019()
def f_019_2( arg1, arg2, arg3 ):
print( "in {}( {}, {}, {} )".format(f_019_2.__name__, arg1, arg2, arg3) )
@my_decorator_019('arg')
def f_019_3( arg1, arg2, arg3 ):
print( "in {}( {}, {}, {} )".format(f_019_3.__name__, arg1, arg2, arg3) )
f_019_1( "Argument 1", "Argument 2", "Argument 3" )
f_019_2( "Argument 1", "Argument 2", "Argument 3" )
f_019_3( "Argument 1", "Argument 2", "Argument 3" )
Pour faire la distinction entre les arguments passés au décorateur et les arguments passés lorsque f_019_ * ()
est appelé, ils se distinguent respectivement par (* args, ** kwargs)
et (* inner_args, ** inner_kwargs)
. Faire.
Résultat d'exécution
$ python sample_019.py
in inner(Argument 1,Argument 2,Argument 3)
in inner(Argument 1,Argument 2,Argument 3)
in inner(Argument 1,Argument 2,Argument 3)
Maintenant, j'aimerais penser qu'un cadre de décorateur à usage général a été créé ... Les noms des fonctions appelées (f_019_ * .__ name__
) sont tous devenus ʻinner`.
Vous pouvez voir que la fonction ʻinner est appelée à la place de la fonction d'origine. Cependant, la fonction d'origine peut faire référence à des attributs tels que name (
name) et documentation (
doc`).
Il existe un décorateur appelé «@ wraps» qui peut éviter cela.
Voir aussi: [@ functools.wraps () --functools --- Manipulation des fonctions d'ordre supérieur et des objets appelables - Documentation Python 3.8.1](https://docs.python.org/ja/3/library/functools.html? highlight = wraps # functools.wraps) Voir aussi: [functools.update_wrapper () --functools --- Manipulation des fonctions d'ordre supérieur et des objets appelables - Documentation Python 3.8.1](https://docs.python.org/ja/3/library/functools.html?highlight = wraps # functools.update_wrapper)
Si vous décorez ceci avant le def inner
, vous pouvez envelopper et conserver les attributs de l'argument func
transmis à la fonction externe (_my_decorator ()
).
sample_020.py
from functools import wraps
def my_decorator_020( *args, **kwargs ):
def _my_decorator( func ):
@wraps(func)
def inner( *inner_args, **inner_kwargs ):
#Écrivez le processus de pré-appel ici
try:
ret = func( *inner_args, **inner_kwargs )
except:
raise
#Écrivez le traitement post-appel ici
return ret
return inner
if len(args) == 1 and callable(args[0]):
return _my_decorator( args[0] )
else:
return _my_decorator
@my_decorator_020
def f_020_1( arg1, arg2, arg3 ):
print( "in {}( {}, {}, {} )".format(f_020_1.__name__, arg1, arg2, arg3) )
@my_decorator_020()
def f_020_2( arg1, arg2, arg3 ):
print( "in {}( {}, {}, {} )".format(f_020_2.__name__, arg1, arg2, arg3) )
@my_decorator_020('arg')
def f_020_3( arg1, arg2, arg3 ):
print( "in {}( {}, {}, {} )".format(f_020_3.__name__, arg1, arg2, arg3) )
f_020_1( "Argument 1", "Argument 2", "Argument 3" )
f_020_2( "Argument 1", "Argument 2", "Argument 3" )
f_020_3( "Argument 1", "Argument 2", "Argument 3" )
Pour sample_019.py
, @wraps (func) ʻest ajouté avant
à partir de functools import wrapset
def inner ().
func est la fonction passée en argument à
_my_decorator ()`.
Résultat d'exécution
$ python sample_020.py
in f_020_1(Argument 1,Argument 2,Argument 3)
in f_020_2(Argument 1,Argument 2,Argument 3)
in f_020_3(Argument 1,Argument 2,Argument 3)
$
Le nom de la fonction (f_019_ * .__ name__
) est conservé comme nom de la fonction d'origine au lieu de ʻinner`.
Ça a été un long chemin, mais finalement le cadre du décorateur est terminé.
Depuis que je l'ai créé de manière générique, c'est def my_decorator (* args, ** kwargs):
, mais si les arguments à recevoir sont fixes, il vaut mieux clarifier.
decorator_framework.py Full Version
decorator_framework.py Full Version
#!/usr/bin/env python
# -*- coding: utf-8 -*-
###########################################
#Décorateur(decorator)
###########################################
from functools import wraps
def my_decorator( *args, **kwargs ):
"""
for doctest
>>> @my_decorator
... def f1( arg1 ):
... print( arg1 )
...
Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#1
No arguments
_my_decorator_body()S'il y a un traitement nécessaire avant de définir, écrivez-le ici
Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#2
>>> @my_decorator('mytest1')
... def f2( arg2 ):
... print( arg2 )
...
Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#1
There are some arguments: ('mytest1',)
_my_decorator_body()S'il y a un traitement nécessaire avant de définir, écrivez-le ici
Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#2
>>> @my_decorator
... def f3( arg1 ):
... print( arg1 )
... a = 1/0
...
Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#1
No arguments
_my_decorator_body()S'il y a un traitement nécessaire avant de définir, écrivez-le ici
Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#2
>>> @my_decorator('mytest2')
... def f4( arg2 ):
... print( arg2 )
... a = 1/0
...
Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#1
There are some arguments: ('mytest2',)
_my_decorator_body()S'il y a un traitement nécessaire avant de définir, écrivez-le ici
Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#2
>>> try:
... f1( "Hello, World! #1" )
... except:
... print( "error #1" )
...
Le prétraitement se fait ici(<function f1 at 0x...>,) {} ('Hello, World! #1',) {}
Hello, World! #1
Le post-traitement se fait ici... {} ('Hello, World! #1',) {}
>>> try:
... f2( "Hello, World! #2" )
... except:
... print( "error #2" )
...
Le prétraitement se fait ici('mytest1',) {} ('Hello, World! #2',) {}
Hello, World! #2
Le post-traitement se fait ici('mytest1',) {} ('Hello, World! #2',) {}
>>> try:
... f3( "Hello, World! #3" )
... except:
... print( "error #3" )
...
Le prétraitement se fait ici(<function f3 at 0x...>,) {} ('Hello, World! #3',) {}
Hello, World! #3
error #3
>>> try:
... f4( "Hello, World! #4" )
... except:
... print( "error #4" )
...
Le prétraitement se fait ici('mytest2',) {} ('Hello, World! #4',) {}
Hello, World! #4
error #4
>>>
"""
#Cliquez ici pour les décorateurs qui ne prennent clairement aucun argument
# _my_Renommer le décorateur et définir globalement
def _my_decorator( func ):
# _my_decorator_body()S'il y a un traitement nécessaire avant de définir, écrivez-le ici
print( "_my_decorator_body()S'il y a un traitement nécessaire avant de définir, écrivez-le ici" )
@wraps(func)
def _my_decorator_body( *body_args, **body_kwargs ):
#Le prétraitement se fait ici
print( "Le prétraitement se fait ici", args, kwargs, body_args, body_kwargs )
try:
#Exécution du corps décoré
ret = func( *body_args, **body_kwargs )
except:
raise
#Le post-traitement se fait ici
print( "Le post-traitement se fait ici", args, kwargs, body_args, body_kwargs )
return ret
#Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#2
print( "Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#2" )
return _my_decorator_body
#C'est la fin du décorateur qui ne prend clairement aucun argument
#Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#1
print( "Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#1" )
if len(args) == 1 and callable( args[0] ):
#Si le décorateur est appelé sans argument, gérez-le ici
print( "No arguments" )
return _my_decorator( args[0] )
else:
#Si le décorateur est appelé avec un argument, traitez-le ici
print( "There are some arguments:", args )
return _my_decorator
###########################################
# unitttest
###########################################
import unittest
from io import StringIO
import sys
class Test_My_Decorator(unittest.TestCase):
def setUp(self):
self.saved_stdout = sys.stdout
self.stdout = StringIO()
sys.stdout = self.stdout
def tearDown(self):
sys.stdout = self.saved_stdout
def test_decorator_noarg(self):
@my_decorator
def t1(arg0):
print( arg0 )
t1("test_decorator_noarg")
import re
s = re.sub('0x[0-9a-f]+', '0x', self.stdout.getvalue())
self.assertEqual(s,
"Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#1\n" +
"No arguments\n" +
"_my_decorator_body()S'il y a un traitement nécessaire avant de définir, écrivez-le ici\n" +
"Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#2\n" +
"Le prétraitement se fait ici(<function Test_My_Decorator.test_decorator_noarg.<locals>.t1 at 0x>,) {} ('test_decorator_noarg',) {}\n" +
"test_decorator_noarg\n" +
"Le post-traitement se fait ici(<function Test_My_Decorator.test_decorator_noarg.<locals>.t1 at 0x>,) {} ('test_decorator_noarg',) {}\n"
)
def test_decorator_witharg(self):
@my_decorator('with arg')
def t1(arg0):
print( arg0 )
t1("test_decorator_witharg")
self.assertEqual(self.stdout.getvalue(),
"Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#1\n" +
"There are some arguments: ('with arg',)\n" +
"_my_decorator_body()S'il y a un traitement nécessaire avant de définir, écrivez-le ici\n" +
"Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#2\n" +
"Le prétraitement se fait ici('with arg',) {} ('test_decorator_witharg',) {}\n" +
"test_decorator_witharg\n" +
"Le post-traitement se fait ici('with arg',) {} ('test_decorator_witharg',) {}\n"
)
def test_functionname(self):
@my_decorator
def t1():
return t1.__name__
f_name = t1()
self.assertEqual( f_name, "t1" )
def test_docattribute(self):
@my_decorator
def t1():
"""Test Document"""
pass
self.assertEqual( t1.__doc__, "Test Document" )
###########################################
# main
###########################################
if __name__ == '__main__':
@my_decorator
def f1( arg1 ):
print( arg1 )
@my_decorator('mytest1')
def f2( arg2 ):
print( arg2 )
@my_decorator
def f3( arg1 ):
print( arg1 )
a = 1/0
@my_decorator('mytest2')
def f4( arg2 ):
print( arg2 )
a = 1/0
try:
f1( "Hello, World! #1" )
except:
print( "error #1" )
try:
f2( "Hello, World! #2" )
except:
print( "error #2" )
try:
f3( "Hello, World! #3" )
except:
print( "error #3" )
try:
f4( "Hello, World! #4" )
except:
print( "error #4" )
import doctest
doctest.testmod(optionflags=doctest.ELLIPSIS)
unittest.main()
Exemple d'exécution
$ python decorator_framework.py
Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#1
No arguments
_my_decorator_body()S'il y a un traitement nécessaire avant de définir, écrivez-le ici
Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#2
Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#1
There are some arguments: ('mytest1',)
_my_decorator_body()S'il y a un traitement nécessaire avant de définir, écrivez-le ici
Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#2
Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#1
No arguments
_my_decorator_body()S'il y a un traitement nécessaire avant de définir, écrivez-le ici
Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#2
Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#1
There are some arguments: ('mytest2',)
_my_decorator_body()S'il y a un traitement nécessaire avant de définir, écrivez-le ici
Si un traitement est nécessaire lorsque le décorateur est décrit, écrivez-le ici#2
Le prétraitement se fait ici(<function f1 at 0x76973a08>,) {} ('Hello, World! #1',) {}
Hello, World! #1
Le post-traitement se fait ici(<function f1 at 0x76973a08>,) {} ('Hello, World! #1',) {}
Le prétraitement se fait ici('mytest1',) {} ('Hello, World! #2',) {}
Hello, World! #2
Le post-traitement se fait ici('mytest1',) {} ('Hello, World! #2',) {}
Le prétraitement se fait ici(<function f3 at 0x7685bd20>,) {} ('Hello, World! #3',) {}
Hello, World! #3
error #3
Le prétraitement se fait ici('mytest2',) {} ('Hello, World! #4',) {}
Hello, World! #4
error #4
....
----------------------------------------------------------------------
Ran 4 tests in 0.005s
OK
$
unittest
$ python -m unittest -v decorator_framework.py
test_decorator_noarg (decorator_framework.Test_My_Decorator) ... ok
test_decorator_witharg (decorator_framework.Test_My_Decorator) ... ok
test_docattribute (decorator_framework.Test_My_Decorator) ... ok
test_functionname (decorator_framework.Test_My_Decorator) ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.006s
OK
$
Recommended Posts