Python >> Tutoriel Python >  >> Python

Comment décorer toutes les fonctions d'une classe sans la taper encore et encore pour chaque méthode ?

Décorez la classe avec une fonction qui parcourt les attributs de la classe et décore les callables. Cela peut être la mauvaise chose à faire si vous avez des variables de classe qui peuvent être appelées et décoreront également des classes imbriquées (crédits à Sven Marnach pour l'avoir signalé), mais c'est généralement une solution plutôt propre et simple. Exemple d'implémentation (notez que cela n'exclura pas les méthodes spéciales (__init__ etc.), que l'on souhaite ou non) :

def for_all_methods(decorator):
    def decorate(cls):
        for attr in cls.__dict__: # there's propably a better way to do this
            if callable(getattr(cls, attr)):
                setattr(cls, attr, decorator(getattr(cls, attr)))
        return cls
    return decorate

Utilisez comme ceci :

@for_all_methods(mydecorator)
class C(object):
    def m1(self): pass
    def m2(self, x): pass
    ...

En Python 3.0 et 3.1, callable n'existe pas. Il existait depuis toujours dans Python 2.x et est de retour dans Python 3.2 en tant que wrapper pour isinstance(x, collections.Callable) , vous pouvez donc l'utiliser (ou définir votre propre callable remplacement en utilisant ceci) dans ces versions.


Bien que je n'aime pas utiliser des approches magiques alors qu'une approche explicite ferait l'affaire, vous pouvez probablement utiliser une métaclasse pour cela.

def myDecorator(fn):
    fn.foo = 'bar'
    return fn

class myMetaClass(type):
    def __new__(cls, name, bases, local):
        for attr in local:
            value = local[attr]
            if callable(value):
                local[attr] = myDecorator(value)
        return type.__new__(cls, name, bases, local)

class myClass(object):
    __metaclass__ = myMetaClass
    def baz(self):
        print self.baz.foo

et cela fonctionne comme si chaque appelable dans myClass avait été décoré de myDecorator

>>> quux = myClass()
>>> quux.baz()
bar

Ne pas faire revivre les choses d'entre les morts, mais j'ai vraiment aimé la réponse de delnan, mais je l'ai trouvé sllliigghhtttlllyy manquant.

def for_all_methods(exclude, decorator):
    def decorate(cls):
        for attr in cls.__dict__:
            if callable(getattr(cls, attr)) and attr not in exclude:
                setattr(cls, attr, decorator(getattr(cls, attr)))
        return cls
    return decorate

EDIT :correction de l'indentation

Vous pouvez donc spécifier des méthodes//attributs//choses que vous ne voulez pas décorer