12
votes

Différence entre les classes de décorateur et les fonctions de décorateur

Je suppose que c'est comme ça qu'elles sont appelées, mais je vais donner des exemples au cas où.

Classe de décorateur: p> xxx pré>

Fonction de décorateur: p>

def decorator(func):
    def wrapper(*args, **kwargs):
        print 'something'
        return func(*args, **kwargs)
    return wrapper


1 commentaires

Il y a une différence. La différence est exactement la même lorsque vous voudriez utiliser une fonction régulière par rapport à une classe normale


3 Réponses :


13
votes

Si vous pouvez écrire une fonction pour implémenter votre décorateur, vous devez le préférer. Mais tous les décorateurs ne peuvent pas être facilement écrits en fonction de la fonction - par exemple lorsque vous souhaitez stocker un état interne.

class counted(object):
    """ counts how often a function is called """
    def __init__(self, func):
        self.func = func
        self.counter = 0

    def __call__(self, *args, **kwargs):
        self.counter += 1
        return self.func(*args, **kwargs)


@counted
def something():
    pass

something()
print something.counter


4 commentaires

Bien sûr, cela peut être écrit en fonction de la fonction. Mais dans Python, vous préférez généralement une classe sur une fermeture pour masquer l'état.


En fait, j'aime la version python (3.x) de ceci (utilise des bretelles * SOB * car les commentaires mangent des pauses de ligne): def compté (f) {x = 0; DEF Wrapper (* args, ** kwargs) {non local x; x + = 1; F (* args, ** kwargs)} retour wrapper} . La version 2.x est laid laide tout à fait la nature (piratage autour du manque de non local avec l'utilisation d'une liste singleton et en utilisant son élément unique).


Vous pouvez également utiliser un attribut de la fonction wrapper pour maintenir le compteur.


@Delnan: Yeah, non utile est très utile, mais dans py2, vous pouvez également écrire def enveloppé (...): enveloppé.Counter + = 1; ... dans le décorateur (les fonctions sont mutables aussi). Je ne peux pas vraiment penser à un bon exemple pour ma réponse; Imaginez simplement quelque chose de plus compliqué ;-)



3
votes

Ce n'est généralement qu'une question de goût. La plupart des programmes Python utilisent la dactylographie du canard et ne se soucient pas vraiment de savoir si la chose qu'ils appellent est une fonction ou une instance d'un autre type, tant qu'elle est appelable. Et tout ce qui avec un __ appel __ () est appelable.

Il y a quelques avantages à utiliser des décorateurs de type fonction:

  • beaucoup plus propre lorsque votre décorateur ne renvoie pas une fonction d'emballage (c'est-à-dire, il renvoie la fonction d'origine après quelque chose, comme définition d'un attribut).

  • Pas besoin d'enregistrer explicitement la référence à la fonction d'origine, car cela se fait par la fermeture.

  • la plupart des outils qui vous aident à créer des décorateurs, tels que functools.arraves () ou Michele Simionato's Signature PRESERVING'S> Decorator Module, fonctionnent avec la fonction Décorateurs de style.

  • Il peut y avoir des programmes quelque part qui n'utilisent pas de dactylographie de canard, mais attendez-vous à un type de fonction, ce qui renvoie une fonction pour remplacer une fonction est théoriquement "plus sûre".

    Pour ces raisons, j'utilise la plupart du temps des décorateurs de style de fonction. En tant que contre exemple, toutefois, Ici est une récente instance dans laquelle le décorateur de style de classe était plus naturel pour moi.


1 commentaires

functools.arras () peut être destiné à fonctionner avec des décorateurs de type fonction, mais functools.update_wrapper (auto, enveloppé) dans le __ init __ de Un décorateur de style de classe fonctionne parfaitement bien.



0
votes

La mise en œuvre de la décoratrice de classe proposée a une légère différence avec la mise en oeuvre de la fonction: elle échouera sur des méthodes xxx

augmentera typeError: myMethod () MyMethod () 1 Argument de positionnement requis: " auto '

Pour ajouter prise en charge des méthodes, vous devez implémenter le __ obtenez __ xxx

va produire < / p>

classe B: ... xxx

La raison pour laquelle cela fonctionne est que lorsque vous accédez au B (). MyMethod , le __ obtenir __ est appelé premier et fournit la méthode liée. Alors __ appel __ est appelé

pour conclure, à condition de définir le __ obtenir __ , la mise en œuvre de la classe et de la fonction peut être utilisé de la même manière. Voir Python Recoke Recette 9.9 pour plus d'informations.


0 commentaires