2009-03-20 5 views
34

Je veux construire des classes pour être utilisés comme décorateurs avec intacts les principes suivants:cours de décorateur en Python

  1. Il devrait être possible d'empiler plusieurs de ces décorateurs de classe au-dessus de 1 fonction.
  2. Le pointeur de nom de fonction résultant doit être impossible à distinguer de la même fonction sans décorateur, sauf peut-être pour quel type/classe il est.
  3. La commande des décorateurs ne devrait être pertinente que si les décorateurs l'exigent. C'est à dire. décorateurs indépendants pourraient être appliqués dans n'importe quel ordre.

C'est un projet Django, et le cas particulier, je travaille maintenant la méthode a besoin de 2 décorateurs, et apparaissent en fonction de python normal:

@AccessCheck 
@AutoTemplate 
def view(request, item_id) {} 

@AutoTemplate change la fonction afin Au lieu de renvoyer une réponse HttpResponse, elle renvoie simplement un dictionnaire à utiliser dans le contexte. Un RequestContext est utilisé et le nom du modèle est déduit du nom et du module de la méthode. @AccessCheck ajoute des contrôles supplémentaires sur l'utilisateur en fonction de l'item_id.

Je suppose que c'est juste pour obtenir le bon constructeur et copier les attributs appropriés, mais quels sont ces attributs?

Le décorateur suivant ne fonctionne pas comme je le décris:

class NullDecl (object): 
    def __init__ (self, func): 
     self.func = func 
    def __call__ (self, * args): 
     return self.func (*args) 

Comme il est démontré par le code suivant:

@NullDecl 
@NullDecl 
def decorated(): 
    pass 

def pure(): 
    pass 

# results in set(['func_closure', 'func_dict', '__get__', 'func_name', 
# 'func_defaults', '__name__', 'func_code', 'func_doc', 'func_globals']) 
print set(dir(pure)) - set(dir(decorated)); 

De plus, essayez d'ajouter "l'impression func nom." Dans le constructeur NullDecl, et il fonctionnera pour le premier décorateur, mais pas le second - car le nom sera manquant.

raffiné eduffy de répondre un peu, et il semble fonctionner assez bien:

class NullDecl (object): 
    def __init__ (self, func): 
     self.func = func 
     for n in set(dir(func)) - set(dir(self)): 
      setattr(self, n, getattr(func, n)) 

    def __call__ (self, * args): 
     return self.func (*args) 
    def __repr__(self): 
     return self.func 
+1

+1 parce que je Je ne sais pas pourquoi quelqu'un a voté cette question. C'est une bonne question: la plupart des questions de décorateur sur SO n'utilisent pas les classes comme décorateurs. Quoi que l'on puisse penser utilise une classe au lieu d'une fonction, c'est une question valide et peut être entièrement appropriée au cas d'utilisation. –

Répondre

21

A ne rien classe décorateur ressemblerait à ceci:

class NullDecl (object): 
    def __init__ (self, func): 
     self.func = func 
     for name in set(dir(func)) - set(dir(self)): 
     setattr(self, name, getattr(func, name)) 

    def __call__ (self, *args): 
     return self.func (*args) 

Et vous peut l'appliquer normalement:

@NullDecl 
def myFunc (x,y,z): 
    return (x+y)/z 
+0

Vérifiez mon commentaire pour savoir pourquoi votre code ne fonctionnera pas comme je l'ai dit. – Staale

+0

J'ai ajouté ma propre correction pour que cela fonctionne correctement. Merci pour l'aide, aide généralement à commencer très basique. – Staale

+6

Peut-être qu'il y a aussi un endroit pour ajouter '** kwargs' à la définition' def __call__ (self, * args): '(et à l'appel de' self.func' plus tard)? –

7

Pour créer un décorateur qui enveloppe les fonctions dans une matière qui les rend indiscernables de la fonction d'origine, utilisez functools.wraps.

Exemple:


def mydecorator(func): 
    @functools.wraps(func): 
    def _mydecorator(*args, **kwargs): 
     do_something() 
     try: 
      return func(*args, **kwargs) 
     finally: 
      clean_up() 
    return _mydecorator 

# ... and with parameters 
def mydecorator(param1, param2): 
    def _mydecorator(func): 
     @functools.wraps(func) 
     def __mydecorator(*args, **kwargs): 
      do_something(param1, param2) 
      try: 
       return func(*args, **kwargs) 
      finally: 
       clean_up() 
     return __mydecorator 
    return _mydecorator 

(ma préférence personnelle est de créer décorateurs utilisant des fonctions, non classes)

L'ordre des décorateurs se présente comme suit:


@d1 
@d2 
def func(): 
    pass 

# is equivalent to 
def func(): 
    pass 

func = d1(d2(func)) 
+0

Je préfère utiliser des classes, mais je suppose que c'est en raison de mon arrière-plan Java. Je pense que j'ai lu quelqu'un recommandant d'utiliser des cours pour les décorateurs, mais je suppose que c'était un mauvais conseil. – Staale

+2

Si vous voulez utiliser des classes, je ne sais pas où vous placez @ functools.wraps. J'ai l'impression que les fonctions de décorateurs sont plus pythoniennes. Et le code est plus court. – codeape

+0

Dans votre exemple de paramètres, d'où vient "func"? Ce n'est pas dans la liste des paramètres de mydecorator() ... –

Questions connexes