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


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


2671
2017-09-25 21:01


origine


Réponses:


Peut-être un peu d'exemple de code aidera: Notez la différence dans les signatures d'appel de foo, class_foo et 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()

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 classmethods, la classe de l'instance d'objet est implicitement transmise comme premier argument au lieu de self.

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

Vous pouvez également appeler class_foo utiliser la classe. En fait, si vous définissez quelque chose à être un classmethod, c'est probablement parce que vous avez l'intention de l'appeler de la classe plutôt que d'une instance de classe. A.foo(1) aurait soulevé un 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é pour les méthodes de classe est de créer constructeurs alternatifs héritables.


Avec staticmethods, ni self (l'instance d'objet) ni cls (la classe) 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 grouper les fonctions qui ont une certaine connexion logique avec une classe à la classe.


foo est juste une fonction, mais quand vous appelez a.foo vous n'obtenez pas seulement la fonction, vous obtenez une version "partiellement appliquée" de la fonction avec l'instance d'objet a lié comme premier argument à la fonction. foo attend 2 arguments, tandis que a.foo attend seulement 1 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_fooplutôt la classe A est lié à class_foo.

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

Ici, avec une méthode statique, même si c'est une méthode, a.static_foo juste retourne une bonne fonction d'ole sans argument lié. static_foo attend 1 argument, et a.static_foo attend 1 argument aussi.

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

Et bien sûr, la même chose arrive quand vous appelez static_foo avec la classe A au lieu.

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

2316
2017-11-03 19:13



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

UNE classmethod, d'autre part, est une méthode qui passe la classe sur laquelle elle a été appelée, ou la classe de l'instance sur laquelle elle a été appelée, en tant que premier argument. C'est utile quand vous voulez que la méthode soit une fabrique pour la classe: puisqu'elle obtient la classe réelle, elle a été appelée comme premier argument, vous pouvez toujours instancier la bonne classe, même quand des sous-classes sont impliquées. Observez par exemple comment dict.fromkeys(), un classmethod, renvoie une instance de la sous-classe lorsqu'elle est appelée dans une sous-classe:

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

654
2017-09-25 21:05



Fondamentalement @classmethod fait une 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.


110
2017-09-25 21:07



Documents officiels python:

@classmethod

Une méthode de classe reçoit la classe en tant que   premier argument implicite, tout comme un   La méthode instance reçoit l'instance.   Pour déclarer une méthode de classe, utilisez cette   idiome:

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

le @classmethod le formulaire est une fonction    décorateur - voir la description de   définitions de fonction dans Fonction   définitions pour plus de détails.

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

Les méthodes de classe sont différentes de C ++   ou des méthodes statiques Java. Si tu veux   ceux-là, voir staticmethod() dans ce   section.

@staticmethod

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

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

le @staticmethod le formulaire est une fonction    décorateur - voir la description de   définitions de fonction dans Fonction   définitions pour plus de détails.

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

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


76
2017-11-03 19:23



Ici est un court article sur cette question

La fonction @staticmethod n'est rien de plus qu'une fonction définie dans une classe. Il est appelable sans instancier la classe en premier. Sa définition est immuable via l'héritage.

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


55
2017-11-03 19:02



Pour décider d'utiliser ou non @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

44
2018-04-22 15:40



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

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

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. Ceci est précisément appelé un "méthode d'instanceLorsqu'une méthode d'instance est utilisée, elle est utilisée comme une fonction partielle (par opposition à une fonction totale, définie pour toutes les valeurs lorsqu'elle est vue dans le code source), c'est-à-dire que le premier des arguments est prédéfini comme instance de l'objet, avec tous ses attributs donnés.Il a l'instance de l'objet lié à lui, et il doit être appelé à partir d'une instance de l'objet.En général, il accède à divers attributs de l'instance.

Par exemple, il s'agit d'une instance d'une chaîne:

', '

si nous utilisons la méthode d'instance, join sur cette chaîne, pour rejoindre un autre itérable, c'est évidemment une fonction de l'instance, en plus d'être une fonction de la liste itérable, ['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 pointée pour une utilisation ultérieure.

Par exemple, cela lie le str.join méthode à la ':' exemple:

>>> join_with_colons = ':'.join 

Et plus tard, nous pouvons l'utiliser comme une fonction qui a déjà le premier argument lié à elle. 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 ne pas prendre l'instance comme argument.

C'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 commodément l'objet par l'importation et l'héritage.

Un exemple de méthode statique est str.maketrans, déplacé de la string module en Python 3. Il rend une table de traduction adaptée à la consommation par str.translate. Il semble plutôt idiot lorsqu'il est utilisé à partir d'une instance d'une chaîne, comme démontré ci-dessous, mais l'importation de la fonction à partir du string le module est plutôt maladroit, et c'est agréable de pouvoir l'appeler de la classe, comme dans str.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 sens qu'elle prend un premier argument implicite, mais au lieu de prendre l'instance, elle prend la classe. Ils sont souvent utilisés comme constructeurs alternatifs pour un meilleur usage sémantique et supporteront 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 ce que sont vos clés et que vous voulez une valeur par défaut pour eux.)

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

Lorsque nous sous-clasons dict, nous pouvons utiliser le même constructeur, 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, voir aussi la documentation officielle de Python sur classmethod et staticmethod.


38
2018-01-23 20:01



@decorators a été ajouté dans python 2.4 Si vous utilisez python <2.4, vous pouvez utiliser la fonction classmethod () et staticmethod ().

Par exemple, si vous voulez créer une méthode 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 que c'est un bon exemple pour utiliser une méthode de classe et une méthode statique, La méthode statique appartient clairement à la classe, car elle utilise la classe Cluster en interne. Le classmethod n'a besoin que d'informations sur la classe et aucune instance de l'objet.

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


27
2018-02-24 09:32



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 une excellente façon 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 performance marginaux, mais je n'ai pas encore vu l'utilisation productive d'une méthode statique dans une classe qui ne pouvait pas être réalisée en tant que fonction autonome en dehors de la classe.


26
2018-05-19 15:27