Disons que ma classe a de nombreuses méthodes, et je veux appliquer mon décorateur sur chacune d'entre elles, plus tard lorsque j'ajouterai 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 all le temps?
Si je regarde dans __call__
est-ce la bonne façon de procéder?
IMPORTANT: l'exemple ci-dessous semble résoudre un problème différent de la question d'origine posée.
EDIT: Je voudrais montrer cette façon, qui est une solution similaire à mon problème pour quiconque trouve 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)
Ensuite, vous pouvez dans un autre
class Wrapmymethodsaround(WrapinMixin):
def __call__:
return super(Wrapmymethodsaround, self).__call__(hey, you, *args)
Décorez la classe avec une fonction qui parcourt les attributs de la classe et décore les callables. Ce n'est peut-être pas la bonne chose à faire si vous avez des variables de classe qui peuvent être appelables et qui décoreront également les classes imbriquées (remerciements à Sven Marnach pour l'avoir signalé), mais généralement c'est une solution plutôt propre et simple. Exemple d'implémentation (notez que cela n'exclura pas les méthodes spéciales (__init__
Etc.), qui peuvent ou non être souhaitées):
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 en Python 2.x et est de retour en Python 3.2 comme wrapper pour isinstance(x, collections.Callable)
, vous pouvez donc l'utiliser (ou définir votre propre remplacement callable
en l'utilisant) dans ces versions.
Bien que je n'aime pas utiliser des approches magiques lorsqu'une approche explicite le ferait, 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é avec myDecorator
>>> quux = myClass()
>>> quux.baz()
bar
Pas pour faire revivre les choses d'entre les morts, mais j'ai vraiment aimé la réponse de Delnan, mais je l'ai trouvée manquante sllliigghhtttlllyy.
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 du retrait
Vous pouvez donc spécifier des méthodes // attributs // des choses que vous ne voulez pas décorer
Aucune des réponses ci-dessus n'a fonctionné pour moi, car je voulais également décorer les méthodes héritées, ce qui n'a pas été accompli en utilisant __dict__
, Et je ne voulais pas trop compliquer les choses avec des métaclasses. Enfin, je suis bien d'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 Là, vous pouvez également voir comment le changer pour Python 3.
Comme le suggèrent les commentaires des autres réponses, envisagez plutôt d'utiliser inspect.getmembers(cls, inspect.isroutine)
. Si vous avez trouvé une solution appropriée qui fonctionne pour les deux Python 2 et Python 3 et décore les méthodes héritées, et peut toujours être fait en 7 lignes, s'il vous plaît, Éditer.
Vous pouvez générer une métaclasse. Cela ne décore 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 dans 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