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



Answers

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.

D'un autre côté, une méthode de classe 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, comme 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
>>> 
Question

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




Pour décider d'utiliser @staticmethod ou @classmethod vous devez regarder dans 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



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

Méthode de classe: Python contrairement à Java et C ++ n'a pas de surcharge de constructeur. Et ainsi, pour y parvenir, vous pouvez utiliser classmethod . l'exemple suivant expliquera ceci

Considérons que nous avons une classe Person qui prend deux arguments last_name et last_name et 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 avez besoin de 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 obtenir la même chose en utilisant @classmethod comme 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 :: Ceci est plutôt simple, il n'est pas lié à l'instance ou la classe et vous pouvez simplement appeler cela en utilisant le nom de la classe.

Alors disons que dans l'exemple ci-dessus vous avez besoin d'une validation que first_name ne 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 le nom de la classe

Person.validate_name("Gaurang Shah")



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

Similarité: Les deux peuvent être appelés sur la classe elle-même, plutôt que simplement sur l' instance de la classe. Donc, les deux sont dans un sens les méthodes de la classe .

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

Donc, une méthode statique n'est, en un sens, pas liée à la classe elle-même et est simplement suspendue juste parce qu'elle peut avoir une fonctionnalité connexe.

>>> 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',)



Analyze @staticmethod literally providing different insights.

A normal method of a class is an implicit dynamic method which takes the instance as first argument.
In contrast, a staticmethod does not take the instance as first argument, so is called 'static' .

A staticmethod is indeed such a normal function the same as those outside a class definition.
It is luckily grouped into the class just in order to stand closer where it is applied, or you might scroll around to find it.




Méthodes statiques

  • Des fonctions simples sans argument de soi.
  • Travailler sur les attributs de classe; pas sur les attributs d'instance.
  • Peut être appelé à travers à la fois 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 la classe
  • Il déplace le code de fonction plus près de l'endroit où il est utilisé
  • Plus pratique à importer par rapport aux fonctions de niveau module, car chaque méthode n'a pas besoin d'être importée spécialement

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

Méthodes de classe:

  • Fonctions qui ont le premier argument en tant que nom de classe.
  • Peut être appelé à travers à la fois la classe et l'instance.
  • Ceux-ci sont créés avec la fonction intégrée de classmethod.

     @classmethod
     def some_class_method(cls, *args, **kwds):
         pass
    



@decorators a été ajouté en 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 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. Le classmethod n'a besoin que d'informations sur la classe et aucune instance de l'objet.

Un autre avantage de faire de la méthode _is_cluster_for un classmethod est qu'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 plusieurs types de cluster, donc il ne suffit pas de vérifier le nom de la classe.




@classmethod signifie : quand cette méthode est appelée, nous passons la classe en tant que premier argument au lieu de l'instance de cette classe (comme nous le faisons normalement avec les méthodes). Cela signifie que vous pouvez utiliser la classe et ses propriétés à l'intérieur de cette méthode plutôt qu'une instance particulière.

@staticmethod signifie: lorsque cette méthode est appelée, nous ne lui transmettons pas une instance de la classe (comme nous le faisons habituellement avec les méthodes). Cela signifie que vous pouvez placer une fonction dans une classe mais vous ne pouvez pas accéder à l'instance de cette classe (ceci est utile lorsque votre méthode n'utilise pas l'instance).




En Python , un classmethod reçoit une classe comme premier argument implicite. La classe de l'instance d'objet est implicitement transmise comme premier argument. Cela peut être utile quand on veut que la méthode soit une usine de la classe car elle obtient la classe réelle (qui a appelé la méthode) comme premier argument, on peut instancier la bonne classe, même si les sous-classes sont aussi concernées.

Une staticmethod est juste une fonction définie dans une classe. Il ne sait rien sur la classe ou l'instance sur laquelle il a été appelé et obtient uniquement les arguments qui ont été passés sans aucun premier argument implicite. Exemple:

class Test(object):
    def foo(self, a):
        print "testing (%s,%s)"%(self,a)

    @classmethod
    def foo_classmethod(cls, a):
        print "testing foo_classmethod(%s,%s)"%(cls,a)

    @staticmethod
    def foo_staticmethod(a):
        print "testing foo_staticmethod(%s)"%a

test = Test()

staticmethods est utilisé pour regrouper les fonctions qui ont une connexion logique avec une classe à la classe.




Documents officiels python:

@classmethod

Une méthode de classe reçoit la classe en tant que 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, ...): ... 

La forme @classmethod est un decorator fonction - voir la description des définitions de fonctions dans les définitions de fonctions pour plus de détails.

Il peut être appelé soit sur la classe (comme Cf() ) ou sur une instance (telle que C().f() ). L'instance est ignorée sauf pour 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 en tant que premier argument implicite.

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

@staticmethod

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

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

La forme @staticmethod est un decorator fonction - voir la description des définitions de fonctions dans les définitions de fonctions pour plus de détails.

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

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




Une autre considération par rapport à staticmethod vs classmethod vient avec l'héritage. Supposons que vous avez la classe suivante:

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

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

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

Cela fonctionne, mais notez que maintenant l'implémentation de bar() dans la classe enfant ( Foo2 ) ne peut plus tirer parti de quelque chose de spécifique à cette classe. Par exemple, disons que Foo2 a une méthode appelée magic() que vous voulez utiliser dans l'implémentation Foo2 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 consiste à appeler Foo2.magic() dans bar() , mais vous vous répétez (si le nom de Foo2 change, vous devrez vous rappeler de mettre à jour cette méthode).

Pour moi, c'est une légère violation du principe ouvert / fermé , car une décision prise dans Foo impact sur votre capacité à refactoriser le code commun dans une classe dérivée (c'est-à-dire qu'elle est moins ouverte à l'extension). Si bar() était une classmethod bien:

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




A quick hack-up ofotherwise identical methods in iPython reveals that @staticmethod yields marginal performance gains (in the nanoseconds), but otherwise it seems to serve no function. Also, any performance gains will probably be wiped out by the additional work of processing the method through staticmethod() during compilation (which happens prior to any code execution when you run a script).

For the sake of code readability I'd avoid @staticmethod unless your method will be used for loads of work, where the nanoseconds count.






Links