Différence entre méthode statique et méthode de classe

3584

Quelle est la différence entre une fonction décorée @staticmethodet une décorée avec @classmethod?

Daryl Spitzer
la source
11
les méthodes statiques sont parfois mieux loties car les fonctions de niveau module en python par souci de propreté. Avec une fonction de module, il est plus facile d'importer uniquement la fonction dont vous avez besoin et d'éviter les "." Inutiles. syntaxe (je vous regarde Objective-C). les méthodes de classe ont plus d'utilité puisqu'elles peuvent être utilisées en combinaison avec le polymorphisme pour créer des fonctions de "modèle d'usine". cela est dû au fait que les méthodes de classe reçoivent la classe comme paramètre implicite.
FistOfFury
27
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.
imsrgadich
4
Conférence
youtube.com/watch?v=HTLu2DFOdTg&feature=youtu.be&t=2689 plus précis, vous n'avez besoin que de la méthode de classe pour les constructeurs alternatifs. Sinon, vous pouvez utiliser la méthode statique et accéder à n'importe quel attribut de classe (via. / Dot) par le "CLASSNAME" en quelque sorte plus informatif au lieu de cls comme dans la méthode de classe
Robert Nowak

Réponses:

3138

Peut-être qu'un petit exemple de code vous aidera à: remarquer la différence dans les signatures d'appel de foo, class_fooet static_foo:

class A(object):
    def foo(self, x):
        print "executing foo(%s, %s)" % (self, x)

    @classmethod
    def class_foo(cls, x):
        print "executing class_foo(%s, %s)" % (cls, x)

    @staticmethod
    def static_foo(x):
        print "executing static_foo(%s)" % x    

a = A()

Vous trouverez ci-dessous la manière habituelle dont une instance d'objet appelle une méthode. L'instance d'objet,, aest implicitement passée 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 d'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 en class_fooutilisant la classe. En fait, si vous définissez quelque chose comme étant une méthode de classe, c'est probablement parce que vous avez l'intention de l'appeler à partir de la classe plutôt qu'à partir d'une instance de classe. A.foo(1)aurait déclenché une TypeError, mais A.class_foo(1)fonctionne très bien:

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

Une utilisation que les gens ont trouvée pour les méthodes de classe est de créer des constructeurs alternatifs héritables .


Avec les méthodes statiques , ni self(l'instance d'objet) ni cls(la classe) n'est implicitement passé comme premier argument. Ils se comportent comme des fonctions simples, 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 des fonctions qui ont une connexion logique avec une classe à la classe.


foon'est qu'une fonction, mais lorsque vous appelez, a.foovous n'obtenez pas seulement la fonction, vous obtenez une version "partiellement appliquée" de la fonction avec l'instance d'objet aliée comme premier argument de la fonction. fooattend 2 arguments, alors qu'il a.foon'attend qu'un seul argument.

aest lié à foo. C'est ce que signifie le terme "lié" ci-dessous:

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

Avec a.class_foo, an'est pas lié à class_foo, plutôt la classe Aest liée à class_foo.

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

Ici, avec une méthode statique, même s'il s'agit d'une méthode, a.static_foorenvoie simplement une bonne fonction 'olé sans aucun argument lié. static_fooattend 1 argument et a.static_fooattend également 1 argument.

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

Et bien sûr, la même chose se produit lorsque vous appelez static_fooà la Aplace avec la classe .

print(A.static_foo)
# <function static_foo at 0xb7d479cc>
unutbu
la source
182
Je ne comprends pas quel est le problème avec l'utilisation de la méthode statique. nous pouvons simplement utiliser une simple fonction hors classe.
Alcott
372
@Alcott: Vous souhaiterez peut-être déplacer une fonction dans une classe car elle appartient logiquement à la classe. Dans le code source Python (par exemple multiprocessing, turtle, dist-packages), il est utilisé pour "masquer" les fonctions "privées" à simple soulignement de l'espace de noms du module. Son utilisation, cependant, est très concentrée dans quelques modules seulement - peut-être une indication que c'est principalement une chose stylistique. Bien que je ne puisse pas trouver d'exemple, cela @staticmethodpourrait aider à organiser votre code en étant surchargeable par les sous-classes. Sans cela, vous auriez des variantes de la fonction flottant dans l'espace de noms du module.
unutbu
15
... ainsi que quelques explications sur et pourquoi utiliser une méthode d'instance, de classe ou statique. Vous n'en avez pas dit un mot, mais le PO ne l'a pas non plus demandé.
MestreLion
106
@Alcott: comme l'a dit unutbu, les méthodes statiques sont une caractéristique organisationnelle / stylistique. Parfois, un module a de nombreuses classes, et certaines fonctions d'assistance sont logiquement liées à une classe donnée et non aux autres, il est donc logique de ne pas "polluer" le module avec de nombreuses "fonctions libres", et il est préférable d'utiliser un statique méthode que de s'appuyer sur le mauvais style de mélange des classes et des définitions de fonctions ensemble dans le code juste pour montrer qu'elles sont "liées"
MestreLion
4
@staticmethodEncore une utilisation pour - vous pouvez l'utiliser pour retirer la cruauté. J'implémente un langage de programmation en Python - les fonctions définies par la bibliothèque utilisent une executeméthode statique , où les fonctions définies par l'utilisateur nécessitent des arguments d'instance (c'est-à-dire le corps de la fonction). Ce décorateur élimine les avertissements "paramètres auto non utilisés" dans l'inspecteur PyCharm.
tehwalrus du
799

Une méthode statique est une méthode qui ne sait rien de la classe ou de l'instance à laquelle elle a été appelée. Il obtient juste les arguments qui ont été passés, pas de premier argument implicite. C'est fondamentalement inutile en Python - vous pouvez simplement utiliser une fonction de module au lieu d'une méthode statique.

Une méthode de classe , en revanche, est une méthode qui obtient la classe à laquelle elle a été appelée, ou la classe de l'instance à laquelle elle a été appelée, comme premier argument. Ceci est utile lorsque vous voulez que la méthode soit une fabrique pour la classe: puisqu'elle obtient la classe réelle à 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 méthode de classe, retourne 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
>>> 
Thomas Wouters
la source
715
Une méthode statique n'est pas inutile - c'est une façon de mettre une fonction dans une classe (car elle y appartient logiquement), tout en indiquant qu'elle ne nécessite pas d'accès à la classe.
Tony Meyer, le
136
D'où seulement «fondamentalement» inutile. Une telle organisation, ainsi que l'injection de dépendances, sont des utilisations valides des méthodes statiques, 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.
Thomas Wouters
40
Quelle est la logique de définir une méthode à l'intérieur d'une classe, alors qu'elle n'a rien à voir avec la classe ou ses instances?
Ben James
107
Peut-être pour l'héritage? Les méthodes statiques peuvent être héritées et remplacées tout comme les méthodes d'instance et les méthodes de classe et la recherche fonctionne comme prévu (contrairement à Java). Les méthodes statiques ne sont pas vraiment résolues statiquement, qu'elles soient appelées sur la classe ou l'instance, donc la seule différence entre la classe et les méthodes statiques est le premier argument implicite.
haridsv
80
Ils créent également un espace de noms plus propre et permettent de comprendre plus facilement que la fonction a quelque chose à voir avec la classe.
Imbrondir
149

Fait essentiellement @classmethodune méthode dont le premier argument est la classe à partir de laquelle elle est appelée (plutôt que l'instance de classe), @staticmethodn'a pas d'arguments implicites.

Terence Simpson
la source
103

Documents officiels sur Python:

@classmethod

Une méthode de classe reçoit la classe comme premier argument implicite, tout comme une méthode d'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 @classmethodformulaire est un décorateur de fonction - voir la description des définitions de fonction dans Définitions de fonction pour plus de détails.

Il peut être appelé soit sur la classe (comme C.f()), soit sur une instance (comme C().f()). L'instance est ignorée à l'exception de sa classe. Si une méthode de classe est appelée pour une classe dérivée, l'objet de classe dérivée est transmis comme premier argument implicite.

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

@staticmethod

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

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

Le @staticmethodformulaire est un décorateur de fonction - voir la description des définitions de fonction dans Définitions de fonction pour plus de détails.

Il peut être appelé soit sur la classe (comme C.f()), soit sur une instance (comme C().f()). L'instance est ignorée à l'exception de sa classe.

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

Chris B.
la source
N'y a-t-il pas une erreur dans les documents? Ne devrait pas être à staticmethod: "L'instance et sa classe sont toutes deux ignorées." au lieu de "L'instance est ignorée sauf pour sa classe."?
mirek
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.
Aaron Bentley
76

Voici un petit article sur cette question

La fonction @staticmethod n'est rien de plus qu'une fonction définie à l'intérieur d'une classe. Il peut être appelé sans instancier la classe en premier. 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, pas la classe Parent, via l'héritage. En effet, le premier argument de la fonction @classmethod doit toujours être cls (classe).

Tom Neyland
la source
1
Cela signifie-t-il donc qu'en utilisant une méthode statique, je suis toujours lié à la classe Parent et avec la méthode de classe, je suis lié à la classe dans laquelle je déclare la méthode de classe (dans ce cas, la sous-classe)?
Mohan Gulati
7
Non. En utilisant une méthode statique, vous n'êtes pas lié du tout; il n'y a pas de premier paramètre implicite. En utilisant classmethod, vous obtenez comme premier paramètre implicite la classe sur laquelle vous avez appelé la méthode (si vous l'avez appelée directement sur une classe), ou la classe de l'instance sur laquelle vous avez appelé la méthode (si vous l'avez appelée sur une instance).
Matt Anderson
6
Pourrait être développé un peu pour montrer qu'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, contrairement aux méthodes statiques (elles auraient besoin de coder en dur MyClass.attr pour cela)
MestreLion
"Sa définition est immuable via l'héritage." n'a aucun sens en Python, vous pouvez très bien remplacer une méthode statique.
cz
68

Pour décider d'utiliser @staticmethod ou @classmethod, vous devez regarder à l'intérieur de votre méthode. Si votre méthode accède à d'autres variables / méthodes de votre classe, utilisez @classmethod . D'un autre côté, si votre méthode ne touche aucune autre partie de la classe, utilisez @staticmethod.

class Apple:

    _counter = 0

    @staticmethod
    def about_apple():
        print('Apple is good for you.')

        # note you can still access other member of the class
        # but you have to use the class instance 
        # which is not very nice, because you have repeat yourself
        # 
        # For example:
        # @staticmethod
        #    print('Number of apples have been juiced: %s' % Apple._counter)
        #
        # @classmethod
        #    print('Number of apples have been juiced: %s' % cls._counter)
        #
        #    @classmethod is especially useful when you move your function to other class,
        #       you don't have to rename the class reference 

    @classmethod
    def make_apple_juice(cls, number_of_apples):
        print('Make juice:')
        for i in range(number_of_apples):
            cls._juice_this(i)

    @classmethod
    def _juice_this(cls, apple):
        print('Juicing %d...' % apple)
        cls._counter += 1
Du D.
la source
quel serait l'avantage de classmethod et cls._counter par rapport à staticmethod et Apple._counter
Robert Nowak
1
cls._counterserait toujours cls._countermême si le code est placé dans une classe différente ou si le nom de la classe est modifié. Apple._counterest spécifique à la Appleclasse; pour une classe différente, ou lorsque le nom de la classe est modifié, vous devrez modifier la classe référencée.
kiamlaluno
53

Quelle est la différence entre @staticmethod et @classmethod en Python?

Vous avez peut-être vu du code Python comme ce pseudocode, qui illustre les signatures des différents types de méthodes et fournit une docstring pour expliquer chacun d'eux:

class Foo(object):

    def a_normal_instance_method(self, arg_1, kwarg_2=None):
        '''
        Return a value that is a function of the instance with its
        attributes, and other arguments such as arg_1 and kwarg2
        '''

    @staticmethod
    def a_static_method(arg_0):
        '''
        Return a value that is a function of arg_0. It does not know the 
        instance or class it is called from.
        '''

    @classmethod
    def a_class_method(cls, arg1):
        '''
        Return a value that is a function of the class and other arguments.
        respects subclassing, it is called with the class it is called from.
        '''

La méthode d'instance normale

Je vais d'abord expliquer a_normal_instance_method. C'est ce qu'on appelle précisément une " méthode d'instance ". Lorsqu'une méthode d'instance est utilisée, elle est utilisée comme fonction partielle (par opposition à une fonction totale, définie pour toutes les valeurs lorsqu'elle est affichée dans le code source) qui est, lorsqu'elle est utilisée, le premier des arguments est prédéfini comme l'instance de la objet, avec tous ses attributs donnés. Il a l'instance de l'objet qui lui est liée et doit être appelée à partir d'une instance de l'objet. En règle générale, il accède à divers attributs de l'instance.

Par exemple, ceci est une instance d'une chaîne:

', '

si nous utilisons la méthode d'instance, joinsur cette chaîne, pour rejoindre un autre itératives, il est bien évidemment fonction de l'instance, en plus d'être une fonction de la liste itératives, ['a', 'b', 'c']:

>>> ', '.join(['a', 'b', 'c'])
'a, b, c'

Méthodes liées

Les méthodes d'instance peuvent être liées via une recherche en pointillés pour une utilisation ultérieure.

Par exemple, cela lie la str.joinméthode à l' ':'instance:

>>> join_with_colons = ':'.join 

Et plus tard, nous pouvons l'utiliser comme une fonction qui a déjà le premier argument lié. De cette façon, cela fonctionne comme une fonction partielle sur l'instance:

>>> join_with_colons('abcde')
'a:b:c:d:e'
>>> join_with_colons(['FF', 'FF', 'FF', 'FF', 'FF', 'FF'])
'FF:FF:FF:FF:FF:FF'

Méthode statique

La méthode statique ne prend pas l'instance comme argument.

Elle est très similaire à une fonction de niveau module.

Cependant, une fonction de niveau module doit vivre dans le module et être spécialement importée vers d'autres endroits où elle est utilisée.

S'il est attaché à l'objet, cependant, il suivra également l'objet de manière pratique à travers l'importation et l'héritage.

Un exemple de méthode statique est str.maketrans, déplacé du stringmodule en Python 3. Il rend une table de traduction adaptée à la consommation de str.translate. Cela semble plutôt idiot lorsqu'il est utilisé à partir d'une instance d'une chaîne, comme illustré ci-dessous, mais l'importation de la fonction depuis le stringmodule est plutôt maladroite, et il est agréable de pouvoir l'appeler à partir de la classe, comme dansstr.maketrans

# demonstrate same function whether called from instance or not:
>>> ', '.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}
>>> str.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}

En python 2, vous devez importer cette fonction à partir du module de chaîne de moins en moins utile:

>>> import string
>>> 'ABCDEFG'.translate(string.maketrans('ABC', 'abc'))
'abcDEFG'

Méthode de classe

Une méthode de classe est similaire à une méthode d'instance en ce qu'elle prend un premier argument implicite, mais au lieu de prendre l'instance, elle prend la classe. Ils sont fréquemment utilisés comme constructeurs alternatifs pour une meilleure utilisation sémantique et prendront en charge l'héritage.

L'exemple le plus canonique d'une méthode de classe intégrée est dict.fromkeys. Il est utilisé comme un constructeur alternatif de dict, (bien adapté lorsque vous savez quelles sont vos clés et que vous voulez une valeur par défaut pour elles.)

>>> dict.fromkeys(['a', 'b', 'c'])
{'c': None, 'b': None, 'a': None}

Lorsque nous sous-classons dict, nous pouvons utiliser le même constructeur, ce qui crée une instance de la sous-classe.

>>> class MyDict(dict): 'A dict subclass, use to demo classmethods'
>>> md = MyDict.fromkeys(['a', 'b', 'c'])
>>> md
{'a': None, 'c': None, 'b': None}
>>> type(md)
<class '__main__.MyDict'>

Voir le code source pandas pour d'autres exemples similaires de constructeurs alternatifs, et voir également la documentation officielle de Python sur classmethodet staticmethod.

Aaron Hall
la source
43

J'ai commencé à apprendre le langage de programmation avec C ++, puis Java et ensuite Python et cette question m'a donc beaucoup dérangé, jusqu'à ce que je comprenne l'utilisation simple de chacun.

Méthode de classe: Python contrairement à Java et C ++ n'a pas de surcharge de constructeur. Et pour cela, vous pouvez utiliser classmethod. L'exemple suivant explique cela

Considérons que nous avons une Personclasse qui prend deux arguments first_nameet last_nameet crée l'instance de Person.

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

Maintenant, si l'exigence vient où vous devez créer une classe en utilisant un seul nom, juste un first_name, vous ne pouvez pas faire quelque chose comme ça en Python.

Cela vous donnera une erreur lorsque vous essaierez de créer un objet (instance).

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    def __init__(self, first_name):
        self.first_name = first_name

Cependant, vous pouvez réaliser la même chose en utilisant @classmethodcomme mentionné ci-dessous

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @classmethod
    def get_person(cls, first_name):
        return cls(first_name, "")

Méthode statique: c'est assez simple, ce n'est pas lié à une instance ou à une classe et vous pouvez simplement l'appeler en utilisant le nom de la classe.

Disons donc que dans l'exemple ci-dessus, vous avez besoin d'une validation qui first_namene doit pas dépasser 20 caractères, vous pouvez simplement le faire.

@staticmethod  
def validate_name(name):
    return len(name) <= 20

et vous pouvez simplement appeler en utilisant class name

Person.validate_name("Gaurang Shah")
Gaurang Shah
la source
2
C'est un vieux post, mais une manière plus pythonique pour que le constructeur accepte un ou deux arguments serait d'utiliser à la def __init__(self, first_name, last_name="")place de classmethod get_person. Le résultat sera également exactement le même dans ce cas.
akarilimano
31

Je pense qu'une meilleure question est "Quand utiliseriez-vous @classmethod vs @staticmethod?"

@classmethod vous permet d'accéder facilement aux membres privés associés à la définition de classe. c'est un excellent moyen de faire des singletons ou des classes d'usine qui contrôlent le nombre d'instances des objets créés.

@staticmethod fournit des gains de performances marginaux, mais je n'ai pas encore vu une utilisation productive d'une méthode statique dans une classe qui ne pourrait pas être réalisée en tant que fonction autonome en dehors de la classe.

Nathan Tregillus
la source
31

@decorators ont été ajoutés en python 2.4 Si vous utilisez python <2.4, vous pouvez utiliser les fonctions classmethod () et staticmethod ().

Par exemple, si vous souhaitez créer une méthode d'usine (une fonction renvoyant une instance d'une implémentation différente d'une classe en fonction de l'argument qu'elle obtient), vous pouvez faire quelque chose comme:

class Cluster(object):

    def _is_cluster_for(cls, name):
        """
        see if this class is the cluster with this name
        this is a classmethod
        """ 
        return cls.__name__ == name
    _is_cluster_for = classmethod(_is_cluster_for)

    #static method
    def getCluster(name):
        """
        static factory method, should be in Cluster class
        returns a cluster object for the given name
        """
        for cls in Cluster.__subclasses__():
            if cls._is_cluster_for(name):
                return cls()
    getCluster = staticmethod(getCluster)

Notez également qu'il s'agit d'un bon exemple d'utilisation d'une méthode de classe et d'une méthode statique. La méthode statique appartient clairement à la classe, car elle utilise la classe Cluster en interne. La méthode de classe n'a besoin que d'informations sur la classe et pas d'instance de l'objet.

Un autre avantage de faire de la _is_cluster_forméthode une méthode de classe est qu'une sous-classe peut décider de modifier son implémentation, peut-être parce qu'elle est assez générique et peut gérer plus d'un type de cluster, donc il ne suffit pas de vérifier le nom de la classe.

Jens Timmerman
la source
28

Méthodes statiques:

  • Fonctions simples sans argument personnel.
  • Travail sur les attributs de classe; pas sur les attributs d'instance.
  • Peut être appelé via la classe et l'instance.
  • La fonction intégrée staticmethod () est utilisée pour les créer.

Avantages des méthodes statiques:

  • Il localise le nom de la fonction dans le classscope
  • Il rapproche le code de fonction de l'endroit où il est utilisé
  • Plus pratique pour importer que pour les fonctions au niveau du module car chaque méthode n'a pas besoin d'être spécialement importée

    @staticmethod
    def some_static_method(*args, **kwds):
        pass

Méthodes de classe:

  • Fonctions qui ont le premier argument comme nom de classe.
  • Peut être appelé via la classe et l'instance.
  • Ils sont créés avec la fonction intégrée classmethod.

     @classmethod
     def some_class_method(cls, *args, **kwds):
         pass
Laxmi
la source
22

@staticmethoddésactive simplement la fonction par défaut en tant que descripteur de méthode. classmethod enveloppe votre fonction dans un conteneur appelable qui transmet une référence à la classe propriétaire comme premier argument:

>>> class C(object):
...  pass
... 
>>> def f():
...  pass
... 
>>> staticmethod(f).__get__(None, C)
<function f at 0x5c1cf0>
>>> classmethod(f).__get__(None, C)
<bound method type.f of <class '__main__.C'>>

En fait, classmethoda une surcharge d'exécution mais permet d'accéder à la classe propriétaire. Alternativement, je recommande d'utiliser une métaclasse et de mettre les méthodes de classe sur cette métaclasse:

>>> class CMeta(type):
...  def foo(cls):
...   print cls
... 
>>> class C(object):
...  __metaclass__ = CMeta
... 
>>> C.foo()
<class '__main__.C'>
Armin Ronacher
la source
1
Un inconvénient possible d'une métaclasse pour cela qui me vient immédiatement à l'esprit est que vous ne pouvez pas appeler la méthode de classe directement sur une instance. c = C(); c.foo()soulève AttributeError, vous auriez à faire type(c).foo(). Cela pourrait également être considéré comme une fonctionnalité - je ne vois pas pourquoi vous voudriez le faire.
Aaron Hall
20

Le guide définitif sur la façon d'utiliser des méthodes statiques, de classe ou abstraites en Python est un bon lien pour cette rubrique, et résumez-le comme suit.

@staticmethodfonction n'est rien de plus qu'une fonction définie à l'intérieur d'une classe. Il peut être appelé sans instancier la classe en premier. Sa définition est immuable via l'héritage.

  • Python n'a pas à instancier une méthode liée pour l'objet.
  • Il facilite la lisibilité du code et ne dépend pas de l'état de l'objet lui-même;

@classmethodLa fonction peut également être appelée sans instancier la classe, mais sa définition suit la sous-classe, pas la classe parent, via l'héritage, peut être remplacée par la sous-classe. C'est parce que le premier argument pour la @classmethodfonction doit toujours être cls (classe).

  • Méthodes d'usine , qui sont utilisées pour créer une instance pour une classe en utilisant par exemple une sorte de prétraitement.
  • Méthodes statiques appelant des méthodes statiques : si vous divisez une méthode statique en plusieurs méthodes statiques, vous ne devez pas coder en dur le nom de la classe mais utiliser des méthodes de classe
zangw
la source
Merci @zangw - l'immuabilité héritée de la fonction statique est la principale différence, il semble
hard_working_ant
18

Seul le premier argument diffère :

  • méthode normale: l'objet courant est automatiquement passé en tant que premier argument (supplémentaire)
  • classmethod: la classe de l'objet courant est automatiquement passée en tant que premier argument (supplémentaire)
  • méthode statique: aucun argument supplémentaire n'est transmis automatiquement. Ce que vous avez transmis à la fonction est ce que vous obtenez.

Plus en détail...

méthode normale

Lorsque la méthode d'un objet est appelée, il reçoit automatiquement un argument supplémentaire selfcomme premier argument. Autrement dit, la méthode

def f(self, x, y)

doit être appelé avec 2 arguments. selfest automatiquement passé, et c'est l'objet lui-même .

méthode de classe

Quand la méthode est décorée

@classmethod
def f(cls, x, y)

l'argument fourni automatiquement n'est pas self , mais la classe de self .

méthode statique

Quand la méthode est décorée

@staticmethod
def f(x, y)

la méthode ne reçoit aucun argument automatique. Il ne reçoit que les paramètres avec lesquels il est appelé.

coutumes

  • classmethod est principalement utilisé pour les constructeurs alternatifs.
  • staticmethodn'utilise pas l'état de l'objet. Il pourrait s'agir d'une fonction externe à une classe. Il n'est mis à l'intérieur de la classe que pour regrouper des fonctions avec des fonctionnalités similaires (par exemple, comme Mathles méthodes statiques de classe Java )
class Point
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @classmethod
    def frompolar(cls, radius, angle):
        """The `cls` argument is the `Point` class itself"""
        return cls(radius * cos(angle), radius * sin(angle))

    @staticmethod
    def angle(x, y):
        """this could be outside the class, but we put it here 
just because we think it is logically related to the class."""
        return atan(y, x)


p1 = Point(3, 2)
p2 = Point.frompolar(3, pi/4)

angle = Point.angle(3, 2)
note bleue
la source
17

Permettez-moi de dire la similitude entre une méthode décorée avec @classmethod vs @staticmethod en premier.

Similarité: deux peuvent être appelés sur la classe elle-même, plutôt que sur l' instance de la classe. Donc, les deux sont en quelque sorte des méthodes de classe .

Différence: une méthode de classe recevra la classe elle-même comme premier argument, contrairement à une méthode statique.

Ainsi, une méthode statique n'est pas, en un sens, liée à la classe elle-même et ne fait que s'y accrocher juste parce qu'elle peut avoir une fonctionnalité associée.

>>> class Klaus:
        @classmethod
        def classmthd(*args):
            return args

        @staticmethod
        def staticmthd(*args):
            return args

# 1. Call classmethod without any arg
>>> Klaus.classmthd()  
(__main__.Klaus,)  # the class gets passed as the first argument

# 2. Call classmethod with 1 arg
>>> Klaus.classmthd('chumma')
(__main__.Klaus, 'chumma')

# 3. Call staticmethod without any arg
>>> Klaus.staticmthd()  
()

# 4. Call staticmethod with 1 arg
>>> Klaus.staticmthd('chumma')
('chumma',)
Selva
la source
11

Une autre considération concernant la méthode statique par rapport à la méthode de classe concerne l'héritage. Disons que vous avez la classe suivante:

class Foo(object):
    @staticmethod
    def bar():
        return "In Foo"

Et vous souhaitez ensuite remplacer bar()dans une classe enfant:

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"

Cela fonctionne, mais notez que maintenant l' bar()implémentation dans la classe enfant ( Foo2) ne peut plus profiter de quelque chose de spécifique à cette classe. Par exemple, supposons que vous disposiez d' Foo2une méthode appelée magic()que vous souhaitez utiliser dans leFoo2 implémentation de bar():

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"
    @staticmethod
    def magic():
        return "Something useful you'd like to use in bar, but now can't" 

La solution de contournement ici serait d'appeler Foo2.magic() à bar(), mais vous vous répétez (si le nom des Foo2modifications, vous devez vous rappeler de mettre à jour cette bar()méthode).

Pour moi, c'est une légère violation du principe ouvert / fermé , car une décision prise dans Fooimpacte votre capacité à refactoriser le code commun dans une classe dérivée (c'est-à-dire qu'il est moins ouvert à l'extension). Sibar() c'était un, classmethodça irait:

class Foo(object):
    @classmethod
    def bar(cls):
        return "In Foo"

class Foo2(Foo):
    @classmethod
    def bar(cls):
        return "In Foo2 " + cls.magic()
    @classmethod
    def magic(cls):
        return "MAGIC"

print Foo2().bar()

Donne: In Foo2 MAGIC

Adam Parkin
la source
7

Je vais essayer d'expliquer la différence de base en utilisant un exemple.

class A(object):
    x = 0

    def say_hi(self):
        pass

    @staticmethod
    def say_hi_static():
        pass

    @classmethod
    def say_hi_class(cls):
        pass

    def run_self(self):
        self.x += 1
        print self.x # outputs 1
        self.say_hi()
        self.say_hi_static()
        self.say_hi_class()

    @staticmethod
    def run_static():
        print A.x  # outputs 0
        # A.say_hi() #  wrong
        A.say_hi_static()
        A.say_hi_class()

    @classmethod
    def run_class(cls):
        print cls.x # outputs 0
        # cls.say_hi() #  wrong
        cls.say_hi_static()
        cls.say_hi_class()

1 - on peut appeler directement des méthodes statiques et de classe sans initialiser

# A.run_self() #  wrong
A.run_static()
A.run_class()

2- La méthode statique ne peut pas appeler la méthode self mais peut appeler d'autres méthodes statiques et de classe

3- La méthode statique appartient à la classe et n'utilisera pas du tout d'objet.

4- Les méthodes de classe ne sont pas liées à un objet mais à une classe.

Rizwan Mumtaz
la source
annonce 2: êtes-vous sûr? Comment la méthode statique pourrait-elle appeler la méthode de classe? Il n'y fait aucune référence (à sa classe).
mirek
7

@classmethod: peut être utilisé pour créer un accès global partagé à toutes les instances créées de cette classe ..... comme la mise à jour d'un enregistrement par plusieurs utilisateurs .... Je l'ai particulièrement trouvé utile lors de la création de singletons également ..: )

@static, méthode: n'a rien à voir avec la classe ou l'instance associée à ... mais pour la lisibilité peut utiliser une méthode statique

vijay
la source
5

Vous voudrez peut-être considérer la différence entre:

Class A:
    def foo():  # no self parameter, no decorator
        pass

et

Class B:
    @staticmethod
    def foo():  # no self parameter
        pass

Cela a changé entre python2 et python3:

python2:

>>> A.foo()
TypeError
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

python3:

>>> A.foo()
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

Ainsi, l'utilisation @staticmethodde méthodes for appelées uniquement directement à partir de la classe est devenue facultative en python3. Si vous souhaitez les appeler à la fois de la classe et de l'instance, vous devez toujours utiliser le @staticmethoddécorateur.

Les autres cas ont été bien couverts par la réponse unutbus.

David Schumann
la source
5

Une méthode de classe reçoit la classe comme premier argument implicite, tout comme une méthode d'instance reçoit l'instance. C'est une méthode qui est liée à la classe et non à l'objet de la classe. Elle a accès à l'état de la classe car elle prend un paramètre de classe qui pointe vers la classe et non l'instance d'objet. Il peut modifier un état de classe qui s'appliquerait à toutes les instances de la classe. Par exemple, il peut modifier une variable de classe qui sera applicable à toutes les instances.

En revanche, une méthode statique ne reçoit pas de premier argument implicite, par rapport aux méthodes de classe ou aux méthodes d'instance. Et ne peut pas accéder ou modifier l'état de classe. Il n'appartient qu'à la classe car du point de vue de la conception, c'est la bonne façon. Mais en termes de fonctionnalité n'est pas lié, lors de l'exécution, à la classe.

à titre indicatif, utilisez des méthodes statiques comme utilitaires, utilisez des méthodes de classe par exemple comme usine. Ou peut-être pour définir un singleton. Et utilisez des méthodes d'instance pour modéliser l'état et le comportement des instances.

J'espère que j'étais clair!

Nicolae Petridean
la source
4

Ma contribution démontre la différence entre @classmethod, @staticmethodet les méthodes d'instance, y compris comment une instance peut indirectement appeler @staticmethod. Mais au lieu d'appeler indirectement un @staticmethoddepuis une instance, le rendre privé peut être plus "pythonique". Obtenir quelque chose à partir d'une méthode privée n'est pas démontré ici, mais c'est fondamentalement le même concept.

#!python3

from os import system
system('cls')
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

class DemoClass(object):
    # instance methods need a class instance and
    # can access the instance through 'self'
    def instance_method_1(self):
        return 'called from inside the instance_method_1()'

    def instance_method_2(self):
        # an instance outside the class indirectly calls the static_method
        return self.static_method() + ' via instance_method_2()'

    # class methods don't need a class instance, they can't access the
    # instance (self) but they have access to the class itself via 'cls'
    @classmethod
    def class_method(cls):
        return 'called from inside the class_method()'

    # static methods don't have access to 'cls' or 'self', they work like
    # regular functions but belong to the class' namespace
    @staticmethod
    def static_method():
        return 'called from inside the static_method()'
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# works even if the class hasn't been instantiated
print(DemoClass.class_method() + '\n')
''' called from inside the class_method() '''

# works even if the class hasn't been instantiated
print(DemoClass.static_method() + '\n')
''' called from inside the static_method() '''
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# >>>>> all methods types can be called on a class instance <<<<<
# instantiate the class
democlassObj = DemoClass()

# call instance_method_1()
print(democlassObj.instance_method_1() + '\n')
''' called from inside the instance_method_1() '''

# # indirectly call static_method through instance_method_2(), there's really no use
# for this since a @staticmethod can be called whether the class has been
# instantiated or not
print(democlassObj.instance_method_2() + '\n')
''' called from inside the static_method() via instance_method_2() '''

# call class_method()
print(democlassObj.class_method() + '\n')
'''  called from inside the class_method() '''

# call static_method()
print(democlassObj.static_method())
''' called from inside the static_method() '''

"""
# whether the class is instantiated or not, this doesn't work
print(DemoClass.instance_method_1() + '\n')
'''
TypeError: TypeError: unbound method instancemethod() must be called with
DemoClass instance as first argument (got nothing instead)
'''
"""
Michael Swartz
la source
2

Les méthodes de classe, comme son nom l'indique, sont utilisées pour apporter des modifications aux classes et non aux objets. Pour apporter des modifications aux classes, ils modifieront les attributs de classe (pas les attributs d'objet), car c'est ainsi que vous mettez à jour les classes. C'est la raison pour laquelle les méthodes de classe prennent la classe (classiquement désignée par «cls») comme premier argument.

class A(object):
    m=54

    @classmethod
    def class_method(cls):
        print "m is %d" % cls.m

Les méthodes statiques, d'autre part, sont utilisées pour effectuer des fonctionnalités qui ne sont pas liées à la classe, c'est-à-dire qu'elles ne liront ni n'écriront pas de variables de classe. Par conséquent, les méthodes statiques ne prennent pas les classes en argument. Ils sont utilisés pour que les classes puissent exécuter des fonctionnalités qui ne sont pas directement liées à l'objectif de la classe.

class X(object):
    m=54 #will not be referenced

    @staticmethod
    def static_method():
        print "Referencing/calling a variable or function outside this class. E.g. Some global variable/function."
Tushar Vazirani
la source
2

Analysez littéralement @staticmethod fournissant des informations différentes.

Une méthode normale d'une classe est une méthode dynamique implicite qui prend l'instance comme premier argument.
En revanche, une méthode statique ne prend pas l'instance comme premier argument, elle est donc appelée «statique» .

Une méthode statique est en effet une fonction aussi normale que celles qui sont en dehors d'une définition de classe.
Heureusement, il est regroupé dans la classe afin de se rapprocher de l'endroit où il est appliqué, ou vous pouvez faire défiler pour le trouver.

Calcul
la source
2

Je pense que donner une version purement Python staticmethodetclassmethod aiderait à comprendre la différence entre eux au niveau du langage.

Les deux sont des descripteurs non liés aux données (il serait plus facile de les comprendre si vous connaissez d' abord les descripteurs ).

class StaticMethod(object):
    "Emulate PyStaticMethod_Type() in Objects/funcobject.c"

    def __init__(self, f):
        self.f = f

    def __get__(self, obj, objtype=None):
        return self.f


class ClassMethod(object):
    "Emulate PyClassMethod_Type() in Objects/funcobject.c"
    def __init__(self, f):
        self.f = f

    def __get__(self, obj, cls=None):
        def inner(*args, **kwargs):
            if cls is None:
                cls = type(obj)
            return self.f(cls, *args, **kwargs)
        return inner
Jacky1205
la source
1

staticmethod n'a pas accès aux attributs de l'objet, de la classe ou des classes parentes dans la hiérarchie d'héritage. Il peut être appelé directement à la classe (sans créer d'objet).

classmethod n'a pas accès aux attributs de l'objet. Il peut cependant accéder aux attributs de la classe et des classes parentes dans la hiérarchie d'héritage. Il peut être appelé directement à la classe (sans créer d'objet). Si elle est appelée sur l'objet, elle est identique à la méthode normale qui n'accède pas self.<attribute(s)>et accèdeself.__class__.<attribute(s)> uniquement.

Pensez que nous avons une classe avec b=2, nous allons créer un objet et le réinitialiser b=4dedans. Staticmethod ne peut accéder à rien de ce qui précède. Classmethod ne peut accéder .b==2qu'à, via cls.b. La méthode normale peut accéder à la fois: .b==4via self.bet .b==2via self.__class__.b.

Nous pourrions suivre le style KISS (garder les choses simples, stupides): n'utilisez pas de méthodes statiques et de méthodes de classe, n'utilisez pas de classes sans les instancier, accédez uniquement aux attributs de l'objet self.attribute(s). Il y a des langues où la POO est implémentée de cette façon et je pense que ce n'est pas une mauvaise idée. :)

mirek
la source
Une chose importante de plus pour les méthodes de classe: Si vous modifiez un attribut dans la méthode de classe, tous les objets existants de cette classe qui ne définissent pas explicitement cet attribut auront la valeur modifiée.
mirek
-4

Un hack-up rapide de méthodes identiques dans iPython révèle que cela @staticmethoddonne des gains de performances marginaux (en nanosecondes), mais sinon il ne semble pas avoir de fonction. En outre, tout gain de performances sera probablement annulé par le travail supplémentaire de traitement de la méthode viastaticmethod() pendant la compilation (qui se produit avant toute exécution de code lorsque vous exécutez un script).

Par souci de lisibilité du code, j'éviterais à @staticmethodmoins que votre méthode ne soit utilisée pour des charges de travail, où les nanosecondes comptent.

Cathal Garvey
la source
7
"Sinon, cela ne semble avoir aucune fonction": pas strictement vrai. Voir la discussion ci-dessus.
Keith Pinson