web-dev-qa-db-fra.com

Quand dois-je utiliser @classmethod et quand la méthode def (self)?

En intégrant une application Django que je n'ai pas utilisée auparavant, j'ai trouvé deux façons différentes de définir les fonctions dans les classes. L'auteur semble les utiliser toutes les deux très intentionnellement. La première est celle que j'utilise moi-même beaucoup:

class Dummy(object):

    def some_function(self,*args,**kwargs):
        do something here
        self is the class instance

L'autre est celui que je n'utilise pas, principalement parce que je ne comprends pas quand l'utiliser, et pourquoi:

class Dummy(object):

    @classmethod
    def some_function(cls,*args,**kwargs):
        do something here
        cls refers to what?

Dans les documents Python, le décorateur classmethod est expliqué avec cette phrase:

Une méthode de classe reçoit la classe comme premier argument implicite, tout comme une méthode d'instance reçoit l'instance.

Donc je suppose que cls fait référence à Dummy lui-même (le class, pas l'instance). Je ne comprends pas exactement pourquoi cela existe, car je pourrais toujours le faire:

type(self).do_something_with_the_class

Est-ce juste pour plus de clarté, ou ai-je raté la partie la plus importante: des choses fantasmagoriques et fascinantes qui ne pourraient pas être faites sans cela?

69
marue

Votre supposition est correcte - vous comprenez comment classmethods fonctionnent.

La raison en est que ces méthodes peuvent être appelées à la fois sur une instance OR sur la classe (dans les deux cas, l'objet classe sera passé comme premier argument):

class Dummy(object):

    @classmethod
    def some_function(cls,*args,**kwargs):
        print cls

#both of these will have exactly the same effect
Dummy.some_function()
Dummy().some_function()

Sur l'utilisation de ceux-ci sur les instances: Il existe au moins deux utilisations principales pour appeler une méthode de classe sur une instance:

  1. self.some_function() appellera la version de some_function sur le type réel de self, plutôt que la classe dans laquelle cet appel apparaît (et n'aura pas besoin d'attention si le est renommée); et
  2. Dans les cas où some_function Est nécessaire pour implémenter un protocole, mais est utile pour appeler uniquement l'objet de classe.

La différence avec staticmethod: Il existe une autre façon de définir des méthodes qui n'accèdent pas aux données d'instance, appelée staticmethod. Cela crée une méthode qui ne reçoit pas du tout un premier argument implicite; en conséquence, aucune information sur l'instance ou la classe sur laquelle il a été appelé ne lui sera transmise.

In [6]: class Foo(object): some_static = staticmethod(lambda x: x+1)

In [7]: Foo.some_static(1)
Out[7]: 2

In [8]: Foo().some_static(1)
Out[8]: 2

In [9]: class Bar(Foo): some_static = staticmethod(lambda x: x*2)

In [10]: Bar.some_static(1)
Out[10]: 2

In [11]: Bar().some_static(1)
Out[11]: 2

L'utilisation principale que j'ai trouvée pour cela est d'adapter une fonction existante (qui ne s'attend pas à recevoir un self) pour être une méthode sur une classe (ou un objet).

63
Marcin