Comment puis-je décorer toutes les fonctions d'une classe sans le taper pour chaque ajout de la méthode? Python [dupliquer]

cette question a déjà une réponse ici:

  • fixation D'un décorateur à toutes les fonctions d'une classe 4 réponses

disons que ma classe a beaucoup de méthodes, et je veux appliquer mon décorateur sur chacun d'eux, plus tard quand j'ajoute de nouvelles méthodes, je veux que le même décorateur soit appliqué, mais je ne veux pas écrire @mydecorator au-dessus de la déclaration de méthode tout le temps?

si je regarde dans __call__ est-ce la bonne façon de faire?

Merci

IMPORTANT: l'exemple ci-dessous semble résoudre un problème différent de la question initiale.

EDIT: Id comme pour montrer de cette façon, ce qui est une solution similaire pour mon problème pour quiconque trouvant cette question plus tard, en utilisant un mixin comme mentionné dans les commentaires.

class WrapinMixin(object):
    def __call__(self, hey, you, *args):
        print 'entering', hey, you, repr(args)
        try:
            ret = getattr(self, hey)(you, *args)
            return ret
        except:
            ret = str(e)
            raise
        finally:
            print 'leaving', hey, repr(ret)

puis vous pouvez dans un autre

class Wrapmymethodsaround(WrapinMixin): 
    def __call__:
         return super(Wrapmymethodsaround, self).__call__(hey, you, *args)
38
demandé sur osa 2011-06-10 18:28:42

5 réponses

décore la classe avec une fonction qui parcourt les attributs de la classe et décore les callables. Ce peut être la mauvaise chose à faire si vous avez des variables de classe qui peuvent arriver à être appelable, et sera également décorer les classes imbriquées (crédits à Sven Marnach pour avoir souligné ce point), mais en général, c'est une solution plutôt propre et simple. Exemple de mise en œuvre (notez que cela n'exclura pas les méthodes spéciales ( __init__ etc.

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

utiliser 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 existe depuis toujours en Python 2.x et est de retour en Python 3.2 comme wrapper pour isinstance(x, collections.Callable) , donc vous pouvez utiliser cela (ou définir votre propre remplacement callable en utilisant ceci) dans ces versions.

45
répondu 2011-06-10 14:44:51

bien que je n'aime pas utiliser des approches magiques quand 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 il fonctionne comme si chaque appel dans myClass avait été décoré avec myDecorator

>>> quux = myClass()
>>> quux.baz()
bar
24
répondu SingleNegationElimination 2011-06-10 14:40:15

pas pour ressusciter les choses d'entre les morts, mais j'ai vraiment aimé la réponse de delnan, mais je l'ai trouvé sllliigghhtttlllyy manquer.

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 la mise en retrait

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

7
répondu nickneedsaname 2012-01-31 22:25:34

aucune des réponses ci-dessus n'a fonctionné pour moi, puisque je voulais aussi décorer les méthodes héritées, ce qui n'a pas été accompli en utilisant __dict__ , et je ne voulais pas compliquer les choses avec des métaclasses. Enfin, je suis d'accord pour avoir une solution pour Python 2, car j'ai juste un besoin immédiat d'ajouter du code de profilage pour mesurer le temps utilisé par toutes les fonctions d'une classe.

import inspect
def for_all_methods(decorator):
    def decorate(cls):
        for name, fn in inspect.getmembers(cls, inspect.ismethod):
            setattr(cls, name, decorator(fn))
        return cls
    return decorate

Source (solution légèrement différente): https://stackoverflow.com/a/3467879/1243926 Vous pouvez également voir comment le changer pour Python 3.

comme le suggèrent les commentaires à d'autres réponses, envisagez plutôt d'utiliser inspect.getmembers(cls, inspect.isroutine) . Si vous avez trouvé une bonne solution qui fonctionne à la fois pour Python 2 et Python 3 et décore méthodes héritées, et peut encore être fait en 7 lignes, s'il vous plaît, éditez.

3
répondu osa 2017-05-23 10:31:14

vous pouvez générer un metaclass. Cela ne décorera pas les méthodes héritées.

def decorating_meta(decorator):
    class DecoratingMetaclass(type):
        def __new__(self, class_name, bases, namespace):
            for key, value in list(namespace.items()):
                if callable(value):
                    namespace[key] = decorator(value)

            return type.__new__(self, class_name, bases, namespace)

    return DecoratingMetaclass

cela générera une métaclasse décorant toutes les méthodes avec la fonction spécifiée. Vous pouvez l'utiliser en Python 2 ou 3 en faisant quelque chose comme ça

def doubling_decorator(f):
    def decorated(*a, **kw):
        return f(*a, **kw) * 2
    return decorated

class Foo(dict):
    __metaclass__ = decorating_meta(doubling_decorator)

    def lookup(self, key):
        return self[key]

d = Foo()
d["bar"] = 5
print(d.lookup("bar")) # prints 10
2
répondu Jeremy Banks 2011-06-10 14:47:30