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)
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.
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
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
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.
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