Dans Python 2.5, existe-t-il un moyen de créer un décorateur qui décore une classe? Plus précisément, je souhaite utiliser un décorateur pour ajouter un membre à une classe et changer le constructeur pour prendre une valeur pour ce membre.
Vous recherchez quelque chose comme ce qui suit (qui a une erreur de syntaxe sur 'class Foo:':
def getId(self): return self.__id
class addID(original_class):
def __init__(self, id, *args, **kws):
self.__id = id
self.getId = getId
original_class.__init__(self, *args, **kws)
@addID
class Foo:
def __init__(self, value1):
self.value1 = value1
if __name__ == '__main__':
foo1 = Foo(5,1)
print foo1.value1, foo1.getId()
foo2 = Foo(15,2)
print foo2.value1, foo2.getId()
Je suppose que ce que je recherche vraiment, c'est un moyen de faire quelque chose comme une interface C # en Python. Je dois changer de paradigme je suppose.
la source
Outre la question de savoir si les décorateurs de classe sont la bonne solution à votre problème:
Dans Python 2.6 et supérieur, il existe des décorateurs de classe avec @ -syntax, vous pouvez donc écrire:
Dans les anciennes versions, vous pouvez le faire d'une autre manière:
Notez cependant que cela fonctionne de la même manière que pour les décorateurs de fonctions, et que le décorateur doit renvoyer la nouvelle classe (ou l'original modifié), ce qui n'est pas ce que vous faites dans l'exemple. Le décorateur addID ressemblerait à ceci:
Vous pouvez ensuite utiliser la syntaxe appropriée pour votre version Python comme décrit ci-dessus.
Mais je suis d'accord avec d'autres pour dire que l'héritage est mieux adapté si vous voulez passer outre
__init__
.la source
Personne n'a expliqué que vous pouvez définir dynamiquement des classes. Vous pouvez donc avoir un décorateur qui définit (et retourne) une sous-classe:
Qui peut être utilisé dans Python 2 (le commentaire de Blckknght qui explique pourquoi vous devriez continuer à faire cela dans 2.6+) comme ceci:
Et en Python 3 comme ça (mais attention à utiliser
super()
dans vos classes):Ainsi, vous pouvez avoir votre gâteau et le manger - héritage et décorateurs!
la source
super
appels dans la classe d'origine. S'il yFoo
avait une méthode nomméefoo
qui l'appelait,super(Foo, self).foo()
elle se répéterait à l'infini, car le nomFoo
est lié à la sous-classe retournée par le décorateur, et non à la classe d'origine (qui n'est accessible par aucun nom). Le sans argument de Python 3super()
évite ce problème (je suppose que via la même magie du compilateur qui lui permet de fonctionner du tout). Vous pouvez également contourner le problème en décorant manuellement la classe sous un nom différent (comme vous l'avez fait dans l'exemple Python 2.5).Ce n'est pas une bonne pratique et il n'y a aucun mécanisme pour le faire à cause de cela. L'héritage est la bonne façon d'accomplir ce que vous voulez.
Jetez un œil à la documentation de la classe .
Un petit exemple:
De cette façon, Boss a tout
Employee
, avec aussi sa propre__init__
méthode et ses propres membres.la source
Je conviens que l'héritage convient mieux au problème posé.
J'ai trouvé cette question très pratique sur les cours de décoration, merci à tous.
Voici quelques exemples supplémentaires , basés sur d'autres réponses, y compris la façon dont l'héritage affecte les choses dans Python 2.7, (et @wraps , qui maintient la docstring de la fonction d'origine, etc.):
Vous souhaitez souvent ajouter des paramètres à votre décorateur:
Les sorties:
J'ai utilisé un décorateur de fonction, car je les trouve plus concis. Voici une classe pour décorer une classe:
Une version plus robuste qui vérifie ces parenthèses et fonctionne si les méthodes n'existent pas sur la classe décorée:
Les
assert
vérifications que le décorateur n'a pas été utilisé sans parenthèses. Si tel est le cas, la classe en cours de décoration est passée aumsg
paramètre du décorateur, ce qui déclenche unAssertionError
.@decorate_if
applique uniquement ledecorator
ifcondition
àTrue
.Les
getattr
,callable
test et@decorate_if
sont utilisés pour que le décorateur ne s'arrête pas si lafoo()
méthode n'existe pas sur la classe en cours de décoration.la source
Il y a en fait une assez bonne implémentation d'un décorateur de classe ici:
https://github.com/agiliq/Django-parsley/blob/master/parsley/decorators.py
Je pense en fait que c'est une mise en œuvre assez intéressante. Comme il sous-classe la classe qu'il décore, il se comportera exactement comme cette classe dans des choses comme les
isinstance
contrôles.Cela a un avantage supplémentaire: il n'est pas rare que l'
__init__
instruction dans un formulaire django personnalisé apporte des modifications ou des ajoutsself.fields
, il est donc préférable que les modificationsself.fields
se produisent après que tout__init__
a été exécuté pour la classe en question.Très intelligent.
Cependant, dans votre classe, vous voulez en fait que la décoration modifie le constructeur, ce que je ne pense pas être un bon cas d'utilisation pour un décorateur de classe.
la source
Voici un exemple qui répond à la question du retour des paramètres d'une classe. De plus, il respecte toujours la chaîne d'héritage, c'est-à-dire que seuls les paramètres de la classe elle-même sont retournés. La fonction
get_params
est ajoutée à titre d'exemple simple, mais d'autres fonctionnalités peuvent être ajoutées grâce au module inspect.la source
Django a
method_decorator
un décorateur qui transforme n'importe quel décorateur en décorateur de méthode, vous pouvez voir comment il est implémenté dansdjango.utils.decorators
:https://github.com/django/django/blob/50cf183d219face91822c75fa0a15fe2fe3cb32d/django/utils/decorators.py#L53
https://docs.djangoproject.com/en/3.0/topics/class-based-views/intro/#decorating-the-class
la source