3

Contrat: Une fonction qui prend fonction en tant qu'argument et renvoie une fonction [c'est-à-dire, une version modifiée (ou identique) de la fonction transmise]. Fonction Passé, voici, square, par exemple.Contrat d'un décorateur

@floatify 
def square(n): 
    return n*n 

est-décorateur suppose de revenir seule version décorée de fonction passée, mais rien d'autre?

+2

Le cas le plus courant consiste à renvoyer une nouvelle fonction (un wrapper) utilise la fonction passée, qui n'est pas une version modifiée de la fonction passée. –

+0

@AlexHall Fonction modifiée, je veux dire, une fonction qui utilise la fonction transmise, dans tous les cas. – overexchange

+1

OK, je voulais juste clarifier parce que parfois un décorateur peut simplement définir des attributs sur la fonction transmise et ensuite le renvoyer, ce que je pensais que vous vouliez dire par une version modifiée. –

Répondre

7

Il est censé renvoyer uniquement une fonction, mais rien ne vous empêche de renvoyer tout ce que vous voulez.

>>> def d(x): 
... return "hello" 
... 
>>> @d 
... def f(): 
... return "world" 
... 
>>> f 
'hello' 
>>> f() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: 'str' object is not callable 
>>> 
+0

Je ne comprends pas, mais j'aime ça. J'ai besoin de lire sur les décorateurs ... – ratskin

+6

@ratskin y compris un décorateur au-dessus de la fonction est le même que l'ajout de 'fonction = décorateur (fonction)' après. –

+0

Rien ne m'arrête, de retourner n'importe quoi, est un moyen facile de mal utiliser l'idée de décorateur. Probablement vous devriez enlever cette phrase, dans votre réponse – overexchange

5

La fonction de décoration doit renvoyer une fonction car tout ce qu'elle renvoie sera lié au nom de la fonction d'origine. Il est donc plutôt déroutant que l'objet retourné ne soit pas une fonction (ou un autre appelable, comme un constructeur de classe, ou une instance de classe appelable).

Généralement, la fonction retournée doit avoir une signature de fonction compatible avec la fonction d'origine, mais je suppose que c'est correct si la fonction retournée prend aussi des arguments supplémentaires. De même, le type de retour de la fonction renvoyée doit être compatible avec celui de la fonction d'origine.

Une fonction décorée est un peu comme une sous-classe de la fonction d'origine, et il est donc logique d'adhérer au Liskov substitution principle.

La fonction de décoration pourrait avoir des effets secondaires: par exemple, il pourrait modifier certains globaux. Cela peut être utile, par exemple à des fins de journalisation; OTOH, les fonctions devraient généralement éviter d'avoir des effets secondaires. FWIW, certains décorateurs de fonctions standard renvoient des appelables non-fonction, le plus commun étant probablement @classmethod.


Il n'y a rien de particulièrement magique à propos des décorateurs. Comme Jared Goguen mentionne dans un commentaire,

@decorator 
def some_function(args): 
    #etc 

est identique à

def some_function(args): 
    #etc 

some_function = decorator(some_function) 

La deuxième forme est un peu plus long, mais plus puissant, puisque vous pouvez choisir de lier la fonction renvoyée à un autre nom , si tu veux. Certaines choses qui peuvent être facilement accomplies en utilisant la syntaxe plus longue peuvent être difficiles sinon carrément impossibles à faire en utilisant la syntaxe @.

+0

[ici] (https://stackoverflow.com/a/34558606/2482744) est un exemple de décorateur qui renvoie un non-fonctionnement d'une manière qui est utile. –

+2

@Alex ??? Ce sont des fonctions ... Ai-je raté quelque chose? –

+2

@JaredGoguen les lambdas sont les décorateurs réels, tandis que 'skip [Unless]' sont des fonctions qui produisent des décorateurs. Ainsi, la fonction décorée peut être remplacée par «None». –

-2

Décorateur peut être utilisé comme

  • décorateur d'enregistrement
  • décorateur fonctionnel
  • décorateur paramétrés
  • décorateur à base de classe-

code dans votre requête est décorateur fonctionnel, qui remplacent fu décoré nction (square) avec wrapper.wrapper effectue les opérations suivantes:

  • appels f(n)
  • float à result applique et le retourne.
def floatify(f): 
    def wrapper(n): 
     result = f(n) 
     return float(result) 
    return wrapper 

contrat de décorateur fonctionnel:

La fonction de remplacement honore habituellement le contrat de la fonction décorée :

  • Encaisser s nombre amme/types de args
  • Résultat de retour de type compatible

La fonction de remplacement doit préserver les métadonnées de la fonction décorée

  • Important pour le débogage et d'autres fins méta-programmation, Utilisation @functools.wraps(f)