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

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

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

Tl;dr: lorsque dois-je utiliser, pourquoi dois-je utiliser, et comment les utiliser?

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

1313
demandé sur martineau 2012-08-29 17:37:33

11 réponses

Bien que classmethod et staticmethod soient 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 ne peut avoir aucun paramètre.

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 de classe, traitant des informations de date (ce sera notre passe-partout):

class Date(object):

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

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

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

Méthode De La Classe

Nous avons quelques tâches qui peuvent être bien faites en utilisant classmethod S.

supposons que nous voulons créer beaucoup de Date instances de classe ayant date informations provenant d'une source externe codée sous forme de chaîne au format 'JJ-mm-AAAA'. Supposons que nous devons le faire à différents endroits dans le code source de notre projet.

Donc ce que nous devons faire ici est:

  1. analysez 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 à 3 éléments constitué de cette variable.
  2. instanciez Date en passant ces valeurs à l'appel d'initialisation.

Cela va regarder comme:

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

A cet effet, C++ peut implémenter une telle fonctionnalité avec une surcharge, mais Python n'a pas cette surcharge. Au lieu de cela, nous pouvons utiliser classmethod. Créons 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 passons en revue les avantages que nous avons ici:

  1. Nous avons implémenté l'analyse de chaîne de date en un seul endroit et elle est réutilisable maintenant.
  2. L'Encapsulation fonctionne bien ici (si vous pensez que vous pourriez implémenter l'analyse de chaîne en tant que fonction unique ailleurs, cette solution correspond beaucoup mieux au paradigme OOP).
  3. cls est un objet qui détient la classe elle-même, pas une instance de la classe. C'est assez cool parce que si nous héritons de notre classe Date, tous les enfants auront également from_string défini.

Méthode Statique

Et 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 faire).

Regardons le cas d'utilisation suivant.

Nous avons une chaîne de date que nous voulons valider en quelque sorte. Cette tâche est également logiquement liée à la classe Date que nous avons utilisée jusqu'à présent, mais ne nécessite pas son instanciation.

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 à partir de l'utilisation de staticmethod, nous n'avons aucun accès à ce qu'est la classe- - - c'est fondamentalement juste 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.

2226
répondu Rostyslav Dzinko 2018-09-07 06:34:22

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

Dans L'exemple ci-dessus, Rostyslav a utilisé @classmethod from_string en tant que fabrique pour créer des objets Date à partir 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

Donc new_year et millenium_new_year sont des instances de Date classe.

Mais, si vous observez attentivement, le processus D'usine est codé en dur pour créer des objets Date quoi qu'il arrive. Cela signifie que même si la classe Date est sous-classée, les sous-classes créeront toujours un objet Date simple (sans aucune propriété de la sous-classe). Voir cela 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 du décorateur @staticmethod utilisé.

Dans la plupart des cas, cela n'est pas souhaitable. Si ce que vous voulez est une méthode D'usine qui est au courant de la classe cela l'a appelé, 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)

, Assure que le class n'est pas codé en dur, mais plutôt appris. cls peut être n'importe quelle sous-classe. Le résultat object sera à juste titre une instance de cls. Nous allons tester cela.

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

736
répondu Yaw Boakye 2017-09-04 12:19:17

@classmethod signifie: Lorsque cette méthode est appelée, nous passons la classe comme 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 dans 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 vous ne pouvez pas accéder à l'instance de cette classe (ceci est utile lorsque votre méthode n'utilise pas l'instance).

215
répondu Simeon Visser 2012-08-29 13:40:24

Quand utiliser chaque

@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 par héritage.

  • Python n'a pas d'instancier un lié-méthode pour objet.
  • , Il 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 fonction appelable 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 de la fonction @classmethod doit toujours être cls (class).

  • 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 devriez pas coder en dur le nom de la classe mais utiliser des méthodes de classe

Ici est un bon lien vers ce sujet.

55
répondu zangw 2018-09-13 09:23:47

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.

Lors de l'utilisation de static, vous voudriez que le comportement reste inchangé entre 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..."
31
répondu Krishnendu Kunti 2017-06-14 22:52:02

Une petite compilation

@méthode statique Un moyen 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 manière qu'il est écrit en dehors de la classe, mais cela ne sert à rien en python car si vous avez besoin d'encapsuler une méthode dans une classe, car cette méthode doit faire partie de cette classe, @staticmethod est utile en cela cas.

@classmethod C'est important quand vous voulez écrire une méthode d'usine et par cet attribut personnalisé (s) peut être attaché dans une classe. Ce ou ces attributs peuvent être remplacés dans la classe héritée.

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

Table

28
répondu SIslam 2015-07-24 20:50:16

Signification 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 instance) obtient l'instance (nous l'appelons généralement self) comme premier argument implicite.
  • Un classe méthode obtient la classe (nous avons l'habitude de l'appeler cls) comme l'implicite premier argument.
  • Un statique méthode obtient pas implicite premier argument (comme un fonction régulière).

Quand dois-je utiliser, pourquoi devrais-je utiliser et comment dois-je utiliser?

Vous n'avez pas besoin de de décorateur. Mais sur le principe que vous devez minimiser le nombre d'arguments aux fonctions (voir clean Coder), ils sont utiles pour faire exactement cela.

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 méthodes d'instance et les méthodes de classe, ne pas accepter au moins un argument est une erreur TypeError, mais ne pas comprendre la sémantique de cet argument est une erreur de l'utilisateur.

(définir some_function ' s, par exemple:

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

Et ça va marcher.)

Recherches pointillées sur les instances et les classes:

Une recherche pointillé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 non-data dans l'espace de noms de classe (méthodes).

Remarque, une recherche pointillée sur une instance est appelée comme ce:

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 en pointillés.

Vous devez accéder aux méthodes d'instance à partir d'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é par parsemée de recherche.

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

Non les arguments sont implicitement donnés. Cette méthode fonctionne comme n'importe quelle 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 dois-je utiliser, pourquoi devrais-je utiliser?

Chacune d'entre elles est progressivement plus restrictive dans les informations qu'elles transmettent à la méthode par rapport aux méthodes d'instance.

Utiliser lorsque vous n'avez pas besoin de l'information.

Cela rend vos fonctions et méthodes plus faciles à raisonner et à unité.

Ce Qui est 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 à comprendre. Ils sont également plus faciles à unittest.

Ils s'apparentent aux méthodes instance, class et static. En gardant à l'esprit que lorsque nous avons une instance, nous avons également sa classe, encore une fois, demandez-vous, ce qui est plus facile à comprendre?:

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 préférés exemples intégrés:

La méthode statique str.maketrans était une fonction dans le module string, mais il est beaucoup plus pratique pour elle d'être accessible depuis l'espace de noms str.

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

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

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

Lorsqu'il est sous-classé, nous voyons 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 quand vous ne le faites pas besoin des arguments de classe ou d'instance, mais la fonction est liée à l'utilisation de l'objet, et il est pratique pour la fonction d'être dans l'espace de noms de l'objet.

Utilisez des méthodes de classe lorsque vous n'avez pas besoin d'informations d'instance, mais que vous avez besoin des informations de classe peut-être 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 pour que les sous-classes puissent être utilisées ici.)

23
répondu Aaron Hall 2018-03-21 14:57:43

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

  • @staticmethod n'a pas besoin d'auto ou cls comme premier paramètre de la méthode
  • @staticmethod et @classmethod la fonction encapsulée peut être appelée par une instance ou une variable de classe
  • @staticmethod la fonction décorée affecte une sorte de 'propriété immuable' de cette sous-classe l'héritage ne peut pas écraser sa fonction de classe de base qui est enveloppée par un décorateur @staticmethod.
  • @classmethod besoin de cls (nom de classe, vous pouvez changer le nom de la variable si vous le souhaitez, 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 modifier l'effet de la fonction de classe de base, c'est-à-dire que @classmethod la fonction de classe de base encapsulée peut être écrasée par différentes sous-classes.
1
répondu Yi Chen 2017-07-25 13:48:58

Une façon légèrement différente de penser à ce sujet qui pourrait être utile pour quelqu'un... Une méthode de classe est utilisée dans une superclasse 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 retourner la même chose quelle que soit la classe enfant que nous appelons.

0
répondu boson 2017-01-24 22:59:50

En bref, @classmehtod transforme une méthode normale en une méthode d'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 @ classmethod, vous devriez travailler pour créer des instances une par une et elles sont scartted.

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

Testez-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 elles sont collectées ensemble.

En conclusion, @ classmethod decorator convertit un classique méthode à une méthode d'usine, en utilisant classmethods permet d'ajouter autant de constructeurs alternatifs que nécessaire.

0
répondu JawSaw 2017-12-12 11:04:13

La méthode de classe 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
répondu y durga prasad 2017-09-02 14:49:07