2010-10-05 5 views
7

Par rapport aux décorateurs appliqués à une fonction, il n'est pas facile de comprendre les décorateurs appliqués à une classe.Décorateurs appliqués à la définition de classe avec Python

@foo 
class Bar(object): 
    def __init__(self, x): 
     self.x = x 
    def spam(self): 
     statements 

Quel est le cas d'utilisation des décorateurs dans une classe? Comment l'utiliser?

Répondre

23

Il remplace la grande majorité des bonnes utilisations classiques pour les métaclasses personnalisées d'une manière beaucoup plus simple. Pensez-y de la façon suivante: rien de directement dans le corps de la classe ne peut faire référence à l'objet de classe, car l'objet de classe n'existe que bien après l'exécution du corps (c'est le travail de la métaclasse pour créer l'objet de classe - habituellement type, pour toutes les classes sans métaclasse personnalisée). Mais le code dans le décorateur de classe s'exécute après l'objet de classe est créé (en effet, avec l'objet de classe comme argument!) Et peut donc parfaitement se référer à cet objet de classe (et doit habituellement le faire)).

Par exemple, pensez à:

def enum(cls): 
    names = getattr(cls, 'names', None) 
    if names is None: 
    raise TypeError('%r must have a class field `names` to be an `enum`!', 
        cls.__name__) 
    for i, n in enumerate(names): 
    setattr(cls, n, i) 
    return cls 

@enum 
class Color(object): 
    names = 'red green blue'.split() 

et vous pouvez maintenant se référer à Color.red, Color.green, & c, plutôt que de 0, 1, etc. (Bien sûr, vous le feriez normalement ajouter encore plus de fonctionnalités à faire "an enum", mais ici, je montre juste la façon simple de mettre une telle fonctionnalité dans un décorateur de classe, plutôt que d'avoir besoin d'une métaclasse personnalisée! -)

+0

@Manoj, content que vous l'ayez aimé, merci de me l'avoir fait savoir! –

6

Un cas d'utilisation que je peux penser est si vous voulez Envelopper toutes les méthodes d'une classe avec un décorateur de fonction. Disons que vous avez le décorateur suivant:

def logit(f): 
    def res(*args, **kwargs): 
     print "Calling %s" % f.__name__ 
     return f(*args, **kwargs) 
    return res 

Et la classe suivante:

>>> class Pointless: 
    def foo(self): print 'foo' 
    def bar(self): print 'bar' 
    def baz(self): print 'baz' 

>>> p = Pointless() 
>>> p.foo(); p.bar(); p.baz() 
foo 
bar 
baz 

Vous pouvez décorer toutes les méthodes:

>>> class Pointless: 
    @logit 
    def foo(self): print 'foo' 
    @logit 
    def bar(self): print 'bar' 
    @logit 
    def baz(self): print 'baz' 

>>> p = Pointless() 
>>> p.foo(); p.bar(); p.baz() 
Calling foo 
foo 
Calling bar 
bar 
Calling baz 
baz 

Mais c'est boiteuse! Au lieu de cela, vous pouvez le faire:

>>> def logall(cls): 
for a in dir(cls): 
    if callable(getattr(cls, a)): 
     setattr(cls, a, logit(getattr(cls, a))) 
return cls 

>>> @logall 
class Pointless: 
    def foo(self): print 'foo' 
    def bar(self): print 'bar' 
    def baz(self): print 'baz' 

>>> p = Pointless() 
>>> p.foo(); p.bar(); p.baz() 
Calling foo 
foo 
Calling bar 
bar 
Calling baz 
baz 

MISE À JOUR: Une version plus générique de logall:

>>> def wrapall(method): 
    def dec(cls): 
     for a in dir(cls): 
      if callable(getattr(cls, a)): 
       setattr(cls, a, method(getattr(cls, a))) 
     return cls 
    return dec 

>>> @wrapall(logit) 
class Pointless: 
     def foo(self): print 'foo' 
     def bar(self): print 'bar' 
     def baz(self): print 'baz' 

>>> p = Pointless() 
>>> p.foo(); p.bar(); p.baz() 
Calling foo 
foo 
Calling bar 
bar 
Calling baz 
baz 
>>> 

La divulgation complète: Je n'ai jamais eu à faire cela et je viens de faire cet exemple vers le haut.

Questions connexes