Question Signification de @classmethod et @staticmethod pour débutant?


Quelqu'un pourrait-il m'expliquer le sens de @classmethod et @staticmethod en python? J'ai besoin de connaître la différence et le sens.

Pour autant que je comprenne, @classmethod dit à une classe que c'est une méthode qui devrait être héritée dans les sous-classes, ou ... quelque chose. Cependant, quel est le point de cela? Pourquoi ne pas simplement définir la méthode de classe sans ajouter @classmethod ou @staticmethod ou tout @ définitions?

tl; dr:  quand devrais-je les utiliser, Pourquoi devrais-je les utiliser, et Comment devrais-je les utiliser?

Je suis assez avancé avec C ++, donc l'utilisation de concepts de programmation plus avancés ne devrait pas poser de problème. N'hésitez pas à me donner un exemple C ++ correspondant si possible.


1251
2017-08-29 13:37


origine


Réponses:


Bien que classmethod et staticmethod sont assez similaires, il y a une légère différence d'utilisation pour les deux entités: classmethod doit avoir une référence à un objet de classe comme premier paramètre, alors que staticmethod peut avoir aucun paramètre du tout.

Exemple

class Date(object):

    def __init__(self, day=0, month=0, year=0):
        self.day = day
        self.month = month
        self.year = year

    @classmethod
    def from_string(cls, date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        date1 = cls(day, month, year)
        return date1

    @staticmethod
    def is_date_valid(date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        return day <= 31 and month <= 12 and year <= 3999

date2 = Date.from_string('11-09-2012')
is_date = Date.is_date_valid('11-09-2012')

Explication

Supposons un exemple d'une classe, traitant de l'information de date (c'est ce qui sera notre plat à cuire sur):

class Date(object):

    def __init__(self, day=0, month=0, year=0):
        self.day = day
        self.month = month
        self.year = year

Cette classe peut évidemment être utilisée pour stocker des informations sur certaines dates (sans informations de fuseau horaire, supposons que toutes les dates sont présentées en UTC).

Ici nous avons __init__, un initialiseur typique d'instances de classe Python, qui reçoit des arguments comme un exemple typique instancemethod, ayant le premier argument non optionnel (self) qui contient une référence à une instance nouvellement créée.

Méthode de classe

Nous avons des tâches qui peuvent être bien faites en utilisant classmethods.

Supposons que nous voulons créer beaucoup de Date instances de classe ayant des informations de date provenant de sources externes codées sous la forme d'une chaîne de format suivant ('jj-mm-aaaa'). Nous devons le faire dans différents endroits de notre code source dans le projet.

Donc ce que nous devons faire ici est:

  1. Parse une chaîne pour recevoir le jour, le mois et l'année sous la forme de trois variables entières ou d'un tuple à trois éléments constitué de cette variable.
  2. Instancier Date en passant ces valeurs à l'appel d'initialisation.

Cela ressemblera à:

day, month, year = map(int, string_date.split('-'))
date1 = Date(day, month, year)

A cet effet, C ++ a une telle fonctionnalité que la surcharge, mais Python n'a pas cette fonctionnalité - alors voici quand classmethod s'applique. Permet de créer un autre "constructeur".

    @classmethod
    def from_string(cls, date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        date1 = cls(day, month, year)
        return date1

date2 = Date.from_string('11-09-2012')

Regardons plus attentivement la mise en œuvre ci-dessus, et examinons les avantages que nous avons ici:

  1. Nous avons implémenté l'analyse de chaîne de date à un endroit et elle est réutilisable maintenant.
  2. L'encapsulation fonctionne bien ici (si vous pensez que vous pourriez implémenter l'analyse syntaxique des chaînes comme une seule fonction ailleurs, cette solution convient mieux au paradigme OOP).
  3. cls est un objet qui tient classe elle-même, pas une instance de la classe. C'est plutôt cool parce que si nous héritons de notre Date classe, tous les enfants auront from_string défini aussi.

Méthode statique

Qu'en est-il de staticmethod? C'est assez similaire à classmethod mais ne prend aucun paramètre obligatoire (comme une méthode de classe ou une méthode d'instance).

Regardons le prochain cas d'utilisation.

Nous avons une chaîne de date que nous voulons valider d'une manière ou d'une autre. Cette tâche est également logiquement liée à Date classe que nous avons utilisé jusqu'à présent, mais ne nécessite toujours pas d'instanciation de celui-ci.

Voici où staticmethod peut être utile. Regardons le prochain morceau de code:

    @staticmethod
    def is_date_valid(date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        return day <= 31 and month <= 12 and year <= 3999

    # usage:
    is_date = Date.is_date_valid('11-09-2012')

Donc, comme nous pouvons le voir de l'utilisation de staticmethod, nous n'avons aucun accès à ce qu'est la classe - il s'agit simplement d'une fonction, appelée syntaxiquement comme une méthode, mais sans accès à l'objet et à ses internes (champs et autres méthodes), alors que classmethod le fait.


2154
2017-08-29 14:03



La réponse de Rostyslav Dzinko est très appropriée. Je pensais que je pourrais mettre en évidence une autre raison que vous devriez choisir @classmethod plus de @staticmethod lorsque vous créez un constructeur supplémentaire.

Dans l'exemple ci-dessus, Rostyslav a utilisé le @classmethod  from_string comme une usine pour créer Date objets provenant de paramètres autrement inacceptables. La même chose peut être faite avec @staticmethod comme indiqué dans le code ci-dessous:

class Date:
  def __init__(self, month, day, year):
    self.month = month
    self.day   = day
    self.year  = year


  def display(self):
    return "{0}-{1}-{2}".format(self.month, self.day, self.year)


  @staticmethod
  def millenium(month, day):
    return Date(month, day, 2000)

new_year = Date(1, 1, 2013)               # Creates a new Date object
millenium_new_year = Date.millenium(1, 1) # also creates a Date object. 

# Proof:
new_year.display()           # "1-1-2013"
millenium_new_year.display() # "1-1-2000"

isinstance(new_year, Date) # True
isinstance(millenium_new_year, Date) # True

Ainsi les deux new_year et millenium_new_year sont des exemples de Date classe.

Mais, si vous observez de près, le processus Factory est codé en dur pour créer Date objets, peu importe quoi. Ce que cela signifie est que même si le Date class est sous-classé, les sous-classes créeront tout de même Date objet (sans aucune propriété de la sous-classe). Voir dans l'exemple ci-dessous:

class DateTime(Date):
  def display(self):
      return "{0}-{1}-{2} - 00:00:00PM".format(self.month, self.day, self.year)


datetime1 = DateTime(10, 10, 1990)
datetime2 = DateTime.millenium(10, 10)

isinstance(datetime1, DateTime) # True
isinstance(datetime2, DateTime) # False

datetime1.display() # returns "10-10-1990 - 00:00:00PM"
datetime2.display() # returns "10-10-2000" because it's not a DateTime object but a Date object. Check the implementation of the millenium method on the Date class

datetime2 n'est pas une instance de DateTime? WTF? Eh bien, c'est à cause de la @staticmethod décorateur utilisé.

Dans la plupart des cas, cela n'est pas souhaitable. Si ce que vous voulez est une méthode Factory qui est au courant de la classe qui l'a appelée, alors @classmethod est ce dont vous avez besoin.

Réécrire le Date.millenium comme (c'est la seule partie du code ci-dessus qui change)

@classmethod
def millenium(cls, month, day):
    return cls(month, day, 2000)

veille à ce que class n'est pas codé en dur mais plutôt appris. cls peut être n'importe quelle sous-classe. La résultante object sera à juste titre une instance de cls. Testons ça.

datetime1 = DateTime(10, 10, 1990)
datetime2 = DateTime.millenium(10, 10)

isinstance(datetime1, DateTime) # True
isinstance(datetime2, DateTime) # True


datetime1.display() # "10-10-1990 - 00:00:00PM"
datetime2.display() # "10-10-2000 - 00:00:00PM"

La raison en est, comme vous le savez maintenant, @classmethod a été utilisé à la place de @staticmethod


712
2018-01-30 13:35



@classmethod signifie: lorsque 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 normalement avec les méthodes). Cela signifie que vous pouvez placer une fonction dans une classe mais que vous ne pouvez pas accéder à l'instance de cette classe (ceci est utile lorsque votre méthode n'utilise pas l'instance).


195
2017-08-29 13:40



@staticmethod La fonction 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.

  • Python n'a pas besoin d'instancier un méthode liée pour objet.
  • Cela facilite la lisibilité du code: voir @staticmethod, nous savons que la méthode ne dépend pas de l'état de l'objet lui-même;

@classmethod function aussi appelable sans instancier la classe, mais sa définition suit Sub classe, pas Parent class, via héritage, peut être surchargé par sous-classe. C'est parce que le premier argument pour @classmethodla fonction doit toujours être cls (class).

  • Méthodes d'usine, qui sont utilisés 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 fractionnez une méthode statique dans plusieurs méthodes statiques, vous ne devez pas coder le nom de la classe mais utiliser des méthodes de classe

ici C'est un bon lien vers ce sujet.


53
2018-04-14 07:12



On utiliserait @classmethod quand il / elle voudrait changer le comportement de la méthode en fonction de la sous-classe qui appelle la méthode. rappelez-vous que nous avons une référence à la classe appelante dans une méthode de classe.

Tout en utilisant statique, vous souhaitez que le comportement reste inchangé à travers les sous-classes

Exemple:

class Hero:

  @staticmethod
  def say_hello():
     print("Helllo...")

  @classmethod
  def say_class_hello(cls):
     if(cls.__name__=="HeroSon"):
        print("Hi Kido")
     elif(cls.__name__=="HeroDaughter"):
        print("Hi Princess")

class HeroSon(Hero):
  def say_son_hello(self):
     print("test  hello")



class HeroDaughter(Hero):
  def say_daughter_hello(self):
     print("test  hello daughter")


testson = HeroSon()

testson.say_class_hello() #Output: "Hi Kido"

testson.say_hello() #Outputs: "Helllo..."

testdaughter = HeroDaughter()

testdaughter.say_class_hello() #Outputs: "Hi Princess"

testdaughter.say_hello() #Outputs: "Helllo..."

27
2017-08-21 07:18



Une petite compilation

@staticmethod Une façon d'écrire une méthode dans une classe sans référence à l'objet sur lequel elle est appelée. Donc pas besoin de passer un argument implicite comme self ou cls. Il est écrit exactement de la même façon qu'en dehors de la classe, mais il n'est pas inutile en python car si vous avez besoin d'encapsuler une méthode dans une classe puisque cette méthode doit faire partie de cette classe, @staticmethod est très pratique dans cette classe Cas.

@classmethod C'est important quand vous voulez écrire une méthode d'usine et par ce (s) attribut (s) personnalisé (s) peut être attaché (e) dans une classe. Cet attribut (s) peut être substitué dans la classe héritée.

Une comparaison entre ces deux méthodes peut être comme ci-dessous

Table


26
2017-07-19 16:43



Sens de @classmethod et @staticmethod?

  • Une méthode est une fonction dans l'espace de noms d'un objet, accessible en tant qu'attribut.
  • Une méthode régulière (c'est-à-dire une instance) obtient l'instance (nous l'appelons généralement self) comme premier argument implicite.
  • UNE classe méthode obtient la classe (nous l'appelons habituellement cls) comme premier argument implicite.
  • UNE statique La méthode n'obtient pas de premier argument implicite (comme une fonction régulière).

quand devrais-je les utiliser, pourquoi devrais-je les utiliser, et comment dois-je les utiliser?

Tu n'as pas avoir besoin soit décorateur. Mais sur le principe que vous devriez réduire le nombre d'arguments aux fonctions (voir Clean Coder), ils sont utiles pour ce faire.

class Example(object):

    def regular_instance_method(self):
        """A function of an instance has access to every attribute of that 
        instance, including its class (and its attributes.)
        Not accepting at least one argument is a TypeError.
        Not understanding the semantics of that argument is a user error.
        """
        return some_function_f(self)

    @classmethod
    def a_class_method(cls):
        """A function of a class has access to every attribute of the class.
        Not accepting at least one argument is a TypeError.
        Not understanding the semantics of that argument is a user error.
        """
        return some_function_g(cls)

    @staticmethod
    def a_static_method():
        """A static method has no information about instances or classes
        unless explicitly given. It just lives in the class (and thus its 
        instances') namespace.
        """
        return some_function_h()

Pour les deux méthodes d'instance et les méthodes de classe, ne pas accepter au moins un argument est un TypeError, mais ne pas comprendre la sémantique de cet argument est une erreur de l'utilisateur.

(Définir some_function, par exemple:

some_function_h = some_function_g = some_function_f = lambda x=None: x

et cela fonctionnera.)

recherches pointées sur les instances et les classes:

Une recherche pointée sur une instance est effectuée dans cet ordre - nous recherchons:

  1. un descripteur de données dans l'espace de noms de classe (comme une propriété)
  2. données dans l'instance __dict__
  3. un descripteur de non-données dans l'espace de noms de classe (méthodes).

Notez qu'une recherche pointée sur une instance est invoquée comme ceci:

instance = Example()
instance.regular_instance_method 

et les méthodes sont des attributs appelables:

instance.regular_instance_method()

méthodes d'instance

L'argument, self, est implicitement donné via la recherche pointée.

Vous devez accéder aux méthodes d'instance à partir des instances de la classe.

>>> instance = Example()
>>> instance.regular_instance_method()
<__main__.Example object at 0x00000000399524E0>

méthodes de classe

L'argument, cls, est implicitement donné via une recherche pointée.

Vous pouvez accéder à cette méthode via une instance ou la classe (ou les sous-classes).

>>> instance.a_class_method()
<class '__main__.Example'>
>>> Example.a_class_method()
<class '__main__.Example'>

méthodes statiques

Aucun argument n'est implicitement donné. Cette méthode fonctionne comme toute fonction définie (par exemple) sur l'espace de noms d'un module, sauf qu'elle peut être recherchée

>>> print(instance.a_static_method())
None

Encore une fois, quand devrais-je les utiliser, pourquoi devrais-je les utiliser?

Chacun d'entre eux est progressivement plus restrictif dans l'information qu'ils transmettent la méthode par rapport aux méthodes d'instance.

Utilisez-les quand vous n'avez pas besoin de l'information.

Cela rend vos fonctions et vos méthodes plus faciles à raisonner et à désintéresser.

Lequel est le plus facile à raisonner?

def function(x, y, z): ...

ou

def function(y, z): ...

ou

def function(z): ...

Les fonctions avec moins d'arguments sont plus faciles à raisonner. Ils sont également plus faciles à unittest.

Ils s'apparentent à des méthodes d'instance, de classe et statiques. En gardant à l'esprit que lorsque nous avons une instance, nous avons aussi sa classe, encore une fois, demandez-vous, ce qui est plus facile à raisonner ?:

def an_instance_method(self, arg, kwarg=None):
    cls = type(self)             # Also has the class of instance!
    ...

@classmethod
def a_class_method(cls, arg, kwarg=None):
    ...

@staticmethod
def a_static_method(arg, kwarg=None):
    ...

Exemples intégrés

Voici quelques-uns de mes exemples intégrés préférés:

le str.maketrans méthode statique était une fonction dans le string module, mais il est beaucoup plus pratique de le rendre accessible depuis le str espace de nommage.

>>> 'abc'.translate(str.maketrans({'a': 'b'}))
'bbc'

le dict.fromkeys La méthode class renvoie un nouveau dictionnaire instancié à partir d'un itérable de clés:

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

En cas de sous-classement, on voit qu'il obtient les informations de classe en tant que méthode de classe, ce qui est très utile:

>>> class MyDict(dict): pass
>>> type(MyDict.fromkeys('abc'))
<class '__main__.MyDict'> 

Mon conseil - Conclusion

Utilisez des méthodes statiques lorsque vous n'avez pas besoin des arguments de classe ou d'instance, mais que la fonction est liée à l'utilisation de l'objet et qu'il est pratique que la fonction figure dans l'espace de nom de l'objet.

Utilisez les méthodes de classe lorsque vous n'avez pas besoin d'informations sur l'instance, mais que vous avez besoin des informations de classe pour ses autres méthodes de classe ou statiques, ou peut-être lui-même en tant que constructeur. (Vous ne coderiez pas la classe de manière à ce que les sous-classes puissent être utilisées ici.)


18
2017-10-24 16:09



Je suis un débutant sur ce site, j'ai lu toutes les réponses ci-dessus, et j'ai obtenu l'information que je veux. Cependant, je n'ai pas le droit d'upvote. Donc, je veux commencer sur StackOverflow avec la réponse telle que je la comprends.

  • @staticmethod n'a pas besoin de soi ou de cls comme premier paramètre de la méthode
  • @staticmethod et @classmethod La fonction enveloppée peut être appelée par une instance ou une variable de classe
  • @staticmethod fonction décorée impact une sorte de 'propriété immuable' que l'héritage de sous-classe ne peut pas remplacer sa fonction de classe de base qui est enveloppée par un @staticmethod décorateur.
  • @classmethod need cls (Nom de la classe, vous pouvez changer le nom de la variable si vous voulez, mais ce n'est pas conseillé) comme premier paramètre de la fonction
  • @classmethod toujours utilisé par la sous-classe, l'héritage de la sous-classe peut changer l'effet de la fonction de la classe de base, c'est-à-dire @classmethod La fonction de classe de base enveloppée peut être écrasée par différentes sous-classes.

1
2017-07-23 11:38



Une manière légèrement différente d'y penser qui pourrait être utile pour quelqu'un ... Une méthode de classe est utilisée dans une super-classe pour définir comment cette méthode doit se comporter lorsqu'elle est appelée par différentes classes enfants. Une méthode statique est utilisée lorsque nous voulons renvoyer la même chose indépendamment de la classe enfant que nous appelons.


0
2018-01-24 22:59



En bref, @classmehtod transforme une méthode normale en méthode usine.

Explorons-le avec un exemple:

class PythonBook:
    def __init__(self, name, author):
        self.name = name
        self.author = author
    def __repr__(self):
        return f'Book: {self.name}, Author: {self.author}'

Sans un @ classmethod, vous devez travailler à créer des instances une par une et ils sont scartés.

book1 = PythonBook('Learning Python', 'Mark Lutz')
In [20]: book1
Out[20]: Book: Learning Python, Author: Mark Lutz
book2 = PythonBook('Python Think', 'Allen B Dowey')
In [22]: book2
Out[22]: Book: Python Think, Author: Allen B Dowey

Comme par exemple avec @classmethod

class PythonBook:
    def __init__(self, name, author):
        self.name = name
        self.author = author
    def __repr__(self):
        return f'Book: {self.name}, Author: {self.author}'
    @classmethod
    def book1(cls):
        return cls('Learning Python', 'Mark Lutz')
    @classmethod
    def book2(cls):
        return cls('Python Think', 'Allen B Dowey')

Essaye-le:

In [31]: PythonBook.book1()
Out[31]: Book: Learning Python, Author: Mark Lutz
In [32]: PythonBook.book2()
Out[32]: Book: Python Think, Author: Allen B Dowey

Voir? Les instances sont créées avec succès dans une définition de classe et sont collectées ensemble.

En conclusion, le décorateur @classmethod convertit une méthode conventionnelle en méthode usine. Utiliser classmethods permet d'ajouter autant de constructeurs que nécessaire.


0
2017-12-12 09:41



La méthode Class peut modifier l'état de la classe, elle est liée à la classe et contient cls comme paramètre.

La méthode statique ne peut pas modifier l'état de la classe, elle est liée à la classe et elle ne connaît pas la classe ou l'instance

class empDetails:
    def __init__(self,name,sal):
        self.name=name
        self.sal=sal
    @classmethod
    def increment(cls,name,none):
        return cls('yarramsetti',6000 + 500)
    @staticmethod
    def salChecking(sal):
        return sal > 6000

emp1=empDetails('durga prasad',6000)
emp2=empDetails.increment('yarramsetti',100)
# output is 'durga prasad'
print emp1.name
# output put is 6000
print emp1.sal
# output is 6500,because it change the sal variable
print emp2.sal
# output is 'yarramsetti' it change the state of name variable
print emp2.name
# output is True, because ,it change the state of sal variable
print empDetails.salChecking(6500)

-2
2017-09-02 14:34