4113 votes

Différence entre staticmethod et classmethod

Quelle est la différence entre une fonction décorée avec @staticmethod et une autre décorée de @classmethod ?

18 votes

En Python, il est parfois préférable que les méthodes statiques soient des fonctions de niveau module pour des raisons de propreté. Avec une fonction de module, il est plus facile d'importer uniquement la fonction dont vous avez besoin et d'éviter toute syntaxe "." inutile (je vous regarde, Objective-C). Les méthodes de classe sont plus utiles car elles peuvent être utilisées en combinaison avec le polymorphisme pour créer des fonctions de type "factory pattern".

39 votes

Tl;dr >> par rapport aux méthodes normales, les méthodes statiques et les méthodes de classe sont également accessibles en utilisant la classe, mais contrairement aux méthodes de classe, les méthodes statiques sont immuables via l'héritage.

4 votes

Exposé connexe de Raymond Hettinger sur le sujet : youtube.com/watch?v=HTLu2DFOdTg

3537voto

unutbu Points 222216

Peut-être qu'un peu de code d'exemple vous aidera : Remarquez la différence dans les signatures d'appel de foo , class_foo y static_foo :

class A(object):
    def foo(self, x):
        print(f"executing foo({self}, {x})")

    @classmethod
    def class_foo(cls, x):
        print(f"executing class_foo({cls}, {x})")

    @staticmethod
    def static_foo(x):
        print(f"executing static_foo({x})")

a = A()

Voici la manière habituelle dont une instance d'objet appelle une méthode. L'instance d'objet, a est implicitement passé comme premier argument.

a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>, 1)

Avec les méthodes de classe la classe de l'instance de l'objet est implicitement passée comme premier argument au lieu de self .

a.class_foo(1)
# executing class_foo(<class '__main__.A'>, 1)

Vous pouvez également appeler class_foo en utilisant la classe. En fait, si vous définissez quelque chose comme étant une classmethod, c'est probablement parce que vous avez l'intention de l'appeler depuis la classe plutôt que depuis une instance de la classe. A.foo(1) aurait soulevé une TypeError, mais A.class_foo(1) fonctionne très bien :

A.class_foo(1)
# executing class_foo(<class '__main__.A'>, 1)

L'une des utilisations que les gens ont trouvé pour les méthodes de classe est la création de Constructeurs alternatifs héritables .


Avec les méthodes statiques ni l'un ni l'autre self (l'instance de l'objet) ni cls (la classe) est implicitement passée comme premier argument. Elles se comportent comme des fonctions ordinaires, sauf que vous pouvez les appeler à partir d'une instance ou de la classe :

a.static_foo(1)
# executing static_foo(1)

A.static_foo('hi')
# executing static_foo(hi)

Les méthodes statiques sont utilisées pour regrouper les fonctions qui ont un lien logique avec une classe dans cette classe.


foo est juste une fonction, mais lorsque vous appelez a.foo vous ne recevez pas seulement la fonction, vous obtenez une version "partiellement appliquée" de la fonction avec l'instance de l'objet. a comme premier argument de la fonction. foo attend 2 arguments, tandis que a.foo n'attend qu'un seul argument.

a est lié à foo . C'est ce que l'on entend par le terme "lié" ci-dessous :

print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>

Avec a.class_foo , a n'est pas lié à class_foo mais plutôt la classe A est lié à class_foo .

print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>

Ici, avec une staticmethod, même si c'est une méthode, a.static_foo renvoie juste une bonne vieille fonction sans arguments. static_foo attend 1 argument, et a.static_foo attend aussi 1 argument.

print(a.static_foo)
# <function static_foo at 0xb7d479cc>

Et bien sûr, la même chose se produit quand on appelle static_foo avec la classe A à la place.

print(A.static_foo)
# <function static_foo at 0xb7d479cc>

216 votes

Je ne comprends pas quel est l'intérêt d'utiliser staticmethod. Nous pouvons simplement utiliser une simple fonction hors classe.

437 votes

@Alcott : Vous pourriez vouloir déplacer une fonction dans une classe parce qu'elle appartient logiquement à la classe. Dans le code source de Python (par exemple multiprocessing, turtle, dist-packages), il est utilisé pour "cacher" les fonctions "privées" mono-underscore de l'espace de noms du module. Son utilisation, cependant, est fortement concentrée dans quelques modules seulement -- peut-être une indication que c'est principalement une chose stylistique. Bien que je n'ai pas pu trouver d'exemple de cela, @staticmethod peut aider à organiser votre code en étant surchargée par les sous-classes. Sans cela, vous auriez des variantes de la fonction flottant dans l'espace de noms du module.

18 votes

... ainsi que quelques explications sur y pourquoi pour utiliser des méthodes d'instance, de classe ou statiques. Vous n'avez pas donné un seul mot à ce sujet, mais le PO n'a pas non plus posé de question à ce sujet.

876voto

Thomas Wouters Points 38811

A Méthode statique est une méthode qui ne sait rien de la classe ou de l'instance sur laquelle elle a été appelée. Elle récupère juste les arguments qui ont été passés, sans premier argument implicite. Elle est fondamentalement inutile en Python -- vous pouvez simplement utiliser une fonction de module au lieu d'une staticmethod.

A méthode de classe est une méthode à laquelle on passe la classe sur laquelle elle a été appelée, ou la classe de l'instance sur laquelle elle a été appelée, comme premier argument. C'est utile lorsque vous voulez que la méthode soit une usine pour la classe : puisqu'elle reçoit la classe réelle sur laquelle elle a été appelée comme premier argument, vous pouvez toujours instancier la bonne classe, même lorsque des sous-classes sont impliquées. Observez par exemple comment dict.fromkeys() une classmethod, renvoie une instance de la sous-classe lorsqu'elle est appelée sur une sous-classe :

>>> class DictSubclass(dict):
...     def __repr__(self):
...         return "DictSubclass"
... 
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>>

778 votes

Une staticmethod n'est pas inutile - c'est un moyen de placer une fonction dans une classe (parce qu'elle y appartient logiquement), tout en indiquant qu'elle ne nécessite pas d'accès à la classe.

149 votes

Il n'est donc que "fondamentalement" inutile. Une telle organisation, ainsi que l'injection de dépendances, sont des utilisations valables des staticmethods, mais comme les modules, et non les classes comme en Java, sont les éléments de base de l'organisation du code en Python, leur utilisation et leur utilité sont rares.

45 votes

Qu'y a-t-il de logique à définir une méthode à l'intérieur d'une classe, alors qu'elle n'a rien à voir avec la classe ou ses instances ?

175voto

Terence Simpson Points 976

En gros, @classmethod crée une méthode dont le premier argument est la classe à partir de laquelle elle est appelée (plutôt que l'instance de la classe), @staticmethod n'a pas d'arguments implicites.

118voto

Chris B. Points 1372

Documentation officielle de python :

@classmethod

Une méthode de classe reçoit la classe comme premier argument implicite, tout comme une instance reçoit l'instance. Pour déclarer une méthode de classe, utilisez cet idiome :

class C:
    @classmethod
    def f(cls, arg1, arg2, ...): ... 

Le site @classmethod la forme est une fonction décorateur - voir la description des définitions de fonctions dans Fonction définitions pour les détails.

Elle peut être appelée soit sur la classe (telle que C.f() ) ou sur une instance (telle que C().f() ). L'instance est ignorée, sauf pour sa classe. Si une méthode de est appelée pour une classe dérivée dérivée, l'objet de la classe dérivée est passé comme premier argument implicite.

Les méthodes de classe sont différentes des méthodes statiques C++ ou les méthodes statiques de Java. Si vous voulez celles-ci, consultez staticmethod() dans cette section.

@staticmethod

Une méthode statique ne reçoit pas de premier argument implicite. Pour déclarer une méthode méthode statique, utilisez cet idiome :

class C:
    @staticmethod
    def f(arg1, arg2, ...): ... 

Le site @staticmethod la forme est une fonction décorateur - voir la description des définitions de fonctions dans Fonction définitions pour les détails.

Elle peut être appelée soit sur la classe (telle que C.f() ) ou sur une instance (telle que C().f() ). L'instance est ignorée, sauf pour sa classe.

Les méthodes statiques en Python sont similaires à celles que l'on trouve en Java ou en C++. Pour un concept plus avancé, voir classmethod() dans cette section.

0 votes

N'y a-t-il pas une erreur dans la documentation ? Il ne devrait pas y avoir de staticmethod : "L'instance et sa classe sont toutes deux ignorées." au lieu de "L'instance est ignorée sauf pour sa classe." ?

0 votes

Il peut s'agir d'une erreur de copier-coller, mais à proprement parler, vous ne pouvez pas appeler une méthode sur une classe si vous ignorez la classe.

92voto

Tom Neyland Points 3772

Aquí est un court article sur cette question

La fonction @staticmethod n'est rien d'autre qu'une fonction définie à l'intérieur d'une classe. Elle est appelable sans instancier la classe au préalable. Sa définition est immuable via l'héritage.

La fonction @classmethod peut également être appelée sans instancier la classe, mais sa définition suit la classe Sub, et non la classe Parent, via l'héritage. C'est parce que le premier argument de la fonction @classmethod doit toujours être cls (classe).

2 votes

Cela signifie-t-il qu'en utilisant une staticmethod je suis toujours lié à la classe Parent et qu'avec la classmethod je suis lié à la classe dans laquelle je déclare la classmethod (dans ce cas la sous-classe) ?

9 votes

Non. En utilisant une staticmethod, vous n'êtes pas du tout lié ; il n'y a pas de premier paramètre implicite. En utilisant une classmethod, vous obtenez comme premier paramètre implicite la classe sur laquelle vous avez appelé la méthode (si vous l'avez appelé directement sur une classe), ou la classe de l'instance sur laquelle vous avez appelé la méthode (si vous l'avez appelé sur une instance).

9 votes

On pourrait développer un peu pour montrer que, en ayant une classe comme premier argument, les méthodes de classe ont un accès direct aux autres attributs et méthodes de classe, alors que les méthodes statiques n'en ont pas (il faudrait coder en dur MyClass.attr pour cela).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X