Je n'ai pas vraiment besoin de faire cela, mais je me demandais simplement s'il existe un moyen de lier un décorateur à toutes les fonctions d'une classe, plutôt que de le spécifier explicitement pour chaque fonction. Je suppose que ça devient alors une sorte d'aspect, plutôt qu'un décorateur et que ça me semble un peu étrange, mais que je pensais à quelque chose comme le timing ou l'authenticité, ce serait plutôt sympa.Associer un décorateur à toutes les fonctions d'une classe
Répondre
Le moyen le plus propre de le faire, ou d'apporter d'autres modifications à une définition de classe, est de définir une métaclasse.
Sinon, il suffit d'appliquer votre décorateur à la fin de la définition de classe:
class Something:
def foo(self): pass
for name, fn in inspect.getmembers(Something):
if isinstance(fn, types.UnboundMethodType):
setattr(Something, name, decorator(fn))
Pour Python 3 remplacer types.UnboundMethodType avec types.FunctionType. En pratique, bien sûr, vous voudrez appliquer votre décorateur plus sélectivement, et dès que vous voulez décorer toutes les méthodes sauf une, vous découvrirez qu'il est plus facile et plus flexible juste pour utiliser la syntaxe du décorateur dans le façon traditionnelle.
Vous pouvez remplacer la méthode __getattr__
. Il ne s'agit pas d'attacher un décorateur, mais il vous permet de retourner une méthode décorée. Vous auriez probablement envie de faire quelque chose comme ceci:
class Eggs(object):
def __getattr__(self, attr):
return decorate(getattr(self, `_` + attr))
Il y a une récursion laid cacher là-bas que vous voulez protéger contre, mais c'est un début.
Ne vous dire '__getattribute__'? –
@JaceBrowning: Non, je ne pense pas. Si Eggs.attr est réellement appelé Eggs._attr, cela fonctionnera. Si vous voulez remplacer les accès à Eggs.attr, qui est un attribut réel, alors peut-être. C'est là que les trucs récursifs entrent en jeu. – nmichaels
Je comprends ce que vous faites maintenant - vos méthodes non décorées commencent toutes par '_'. Vous pouvez éviter cela en utilisant '__getattribute__', mais vous avez raison, il y a une récursion difficile à éviter. –
Chaque fois que vous pensez changer de définition de classe, vous pouvez utiliser le décorateur de classe ou la métaclasse. par exemple. en utilisant métaclasse
import types
class DecoMeta(type):
def __new__(cls, name, bases, attrs):
for attr_name, attr_value in attrs.iteritems():
if isinstance(attr_value, types.FunctionType):
attrs[attr_name] = cls.deco(attr_value)
return super(DecoMeta, cls).__new__(cls, name, bases, attrs)
@classmethod
def deco(cls, func):
def wrapper(*args, **kwargs):
print "before",func.func_name
result = func(*args, **kwargs)
print "after",func.func_name
return result
return wrapper
class MyKlass(object):
__metaclass__ = DecoMeta
def func1(self):
pass
MyKlass().func1()
Sortie:
before func1
after func1
Note: il ne sera pas décorer staticmethod et classmethod
Grande info. J'ai utilisé cette technique pour créer des méthodes de test à [Gold/approuvé fichier testant toutes les méthodes dans une classe de test contre chaque fichier dans un répertoire via la métaprogrammation de métaclasse en Python] (https://gist.github.com/patkujawa-wf/1f569d245bbfc73f83a3) . – Pat
Je ne comprends pas vraiment la différence entre '__new__' et' __init__ 'quand il s'agit de métaclasses. Les deux semblent fonctionner pour ce problème, bien qu'ils aient des arguments différents. – Pat
@Pat, il est similaire à '__new__' et' __init__' pour une classe normale, '__new__' est appelé pour construire un objet (classe dans ce cas),' __init__' est appelé pour initialiser cet objet (classe dans ce cas) , donc dans la plupart des cas, cela n'a pas d'importance à moins que vous ayez besoin de faire quelque chose avant ou après la création de classe/obj. –
Bien sûr que les méta-classes sont la façon la plus pythonique aller quand vous voulez modifier la façon ce python crée les objets. Ce qui peut être fait en remplaçant la méthode __new__
de votre classe. Mais il y a quelques points autour de ce problème (spécialement pour 3.X python) que je voudrais mentionner:
types.FunctionType
ne protège pas les méthodes spéciales d'être décorée, car ils sont les types de fonctions. De façon plus générale, vous pouvez simplement décorer les objets dont les noms ne sont pas démarrés avec un double trait de soulignement (__
). Un autre avantage de cette méthode est qu'elle couvre également les objets qui existent dans l'espace de noms et commence par__
mais sont fonctionne pas comme__qualname__
,__module__
, etc.L'argument
namespace
en-tête de__new__
ne contient pas les attributs de classe au sein du__init__
. La raison est que le__new__
s'exécute avant le__init__
(initialisation).Il n'est pas nécessaire d'utiliser un
classmethod
comme décorateur, car la plupart du temps, vous importez votre décorateur depuis un autre module.- Si votre classe est contient un élément global (hors côté de la
__init__
) pour avoir refusé d'être décoré à côté de vérifier si le nom ne démarre pas avec__
vous pouvez vérifier le type avectypes.FunctionType
pour être sûr que vous n'êtes pas décorer un objet non-fonction.
Voici un metacalss exemple que vous pouvez utiliser:
class TheMeta(type):
def __new__(cls, name, bases, namespace, **kwds):
# if your decorator is a class method of the metaclass use
# `my_decorator = cls.my_decorator` in order to invoke the decorator.
namespace = {k: v if k.startswith('__') else my_decorator(v) for k, v in namespace.items()}
return type.__new__(cls, name, bases, namespace)
Démo:
def my_decorator(func):
def wrapper(self, arg):
# You can also use *args instead of (self, arg) and pass the *args
# to the function in following call.
return "the value {} gets modified!!".format(func(self, arg))
return wrapper
class TheMeta(type):
def __new__(cls, name, bases, namespace, **kwds):
# my_decorator = cls.my_decorator (if the decorator is a classmethod)
namespace = {k: v if k.startswith('__') else my_decorator(v) for k, v in namespace.items()}
return type.__new__(cls, name, bases, namespace)
class MyClass(metaclass=TheMeta):
# a = 10
def __init__(self, *args, **kwargs):
self.item = args[0]
self.value = kwargs['value']
def __getattr__(self, attr):
return "This class hasn't provide the attribute {}.".format(attr)
def myfunction_1(self, arg):
return arg ** 2
def myfunction_2(self, arg):
return arg ** 3
myinstance = MyClass(1, 2, value=100)
print(myinstance.myfunction_1(5))
print(myinstance.myfunction_2(2))
print(myinstance.item)
print(myinstance.p)
Sortie:
the value 25 gets modified!!
the value 8 gets modified!!
1
This class hasn't provide the attribute p. # special method is not decorated.
Pour vérifier le 3 i tem des notes ci-dessus, vous pouvez décommenter la ligne a = 10
et faire print(myinstance.a)
et voir le résultat puis changer la compréhension dans le dictionnaire __new__
comme suit voyez le résultat à nouveau:
namespace = {k: v if k.startswith('__') and not isinstance(v, types.FunctionType)\
else my_decorator(v) for k, v in namespace.items()}
- 1. Xquery Toutes les fonctions
- 2. php - variables globales dans toutes les fonctions de classe
- 3. Classe décorateur pour tester les variables de classe requises
- 4. Comment utiliser correctement un décorateur en classe?
- 5. Ajouter un décorateur de propriétés à la classe partielle
- 6. Comment lister toutes les fonctions dans un module Python?
- 7. Utilisation des fonctions de décorateur python d'un module différent
- 8. Comment associer un bouton à un contrôle?
- 9. Existe-t-il un moyen de parcourir et d'exécuter toutes les fonctions d'une classe Python?
- 10. Ajout de fonctionnalités: sous-classe vs décorateur
- 11. Bootstrap toutes les fonctions en PHP
- 12. Puis-je tracer toutes les fonctions/méthodes s'exécutant dans un script Python?
- 13. JQuery - Obtenir toutes les fonctions JS
- 14. Associer System.Drawing.Graphics à un contexte de périphérique?
- 15. Ajout d'un argument à un décorateur
- 16. Décorateur avec classe de base générique
- 17. Aucun argumentateur décorateur décorateur?
- 18. Récupère toutes les fonctions définies pour un objet
- 19. Trouver toutes les fonctions décorées dans un module
- 20. Python: Comment accéder à l'instance d'une classe décorée depuis un décorateur de classe?
- 21. Comment puis-je transmettre tous les paramètres à un décorateur?
- 22. Accédez à un sous-ensemble des fonctions d'une classe Python
- 23. Motif décorateur à grain fin
- 24. Consigner toutes les invocations de fonctions sur la console
- 25. Comment utiliser un décorateur?
- 26. Comment associer un fichier nib (.xib) à un UIView?
- 27. Comment passer un argument spécifique à un décorateur en python
- 28. Comment appliquer une classe à un décorateur Zend Framework en cas d'erreur?
- 29. Accéder à un décorateur dans une classe parente à partir de l'enfant en Python
- 30. Classe PHP globale dans les fonctions?
Je suis tout à fait d'accord, je suis aussi beaucoup plus à l'aise avec la décoration explicite de chaque fonction plutôt qu'un décorateur implicite magique. Était juste curieux est tout. – dochead