statique - staticmethod python




Signification de @classmethod et @staticmethod pour débutant? (8)

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 (ie instance) obtient l'instance (nous l'appelons habituellement self ) comme le premier argument implicite.
  • Une méthode de classe obtient la classe (on l'appelle habituellement cls ) comme premier argument implicite.
  • Une méthode statique 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?

Vous n'avez pas besoin de 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 une 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:

La méthode statique str.maketrans était une fonction du module de string , mais il est beaucoup plus pratique qu'elle soit accessible à partir de 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}

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

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.

Autant que je comprends, @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 aucune @ définition?

tl; dr: quand devrais-je les utiliser, pourquoi devrais-je les utiliser, et comment dois-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.


Bien que classmethod et staticmethod soient assez similaires, il existe 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 pas avoir de 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 initialisateur typique des instances de classe Python, qui reçoit des arguments comme une méthode d' instancemethod typique, ayant le premier argument non-optionnel ( self ) qui contient une référence à une instance nouvellement créée.

Méthode de classe

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

Supposons que nous voulons créer un grand nombre d'instances de classe Date ayant des informations de date provenant de sources externes codées comme 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é - donc c'est ici que 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 pouvez implémenter l'analyse de chaînes comme une seule fonction ailleurs, cette solution convient mieux au paradigme OOP).
  3. cls est un objet qui contient la classe elle-même , pas une instance de la classe. C'est plutôt cool parce que si nous héritons de notre classe Date , tous les enfants auront aussi la fonction from_string .

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 à la classe Date nous avons utilisée jusqu'à présent, mais ne nécessite pas d'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 de l'utilisation de staticmethod , nous n'avons aucun accès à ce que la classe est - c'est simplement 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.


J'ai essayé d'écrire un exemple simple pour montrer la différence entre l'utilisation de staticmethod et classmethod (et aussi de ne pas les utiliser). Voici le code:

#without decorator:
class Duck(object):
    def sound(self):
        print("Quack")


#pay attention to the "sound" function:

#with @staticmethod
class Cat(object):
    @staticmethod
    def sound():
        print("Meow")

#with @classmethod
class Dog(object):
    @classmethod
    def sound(self):
        print("woof")


#notice the differences between calling them:
Duck().sound()
Cat.sound()
Dog.sound()

#prints:
"""
Quack
Meow
woof
"""

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 avec la réponse telle que je la comprends.

  • @staticmethod n'a pas besoin de self ou de cls comme premier paramètre de la méthode
  • @staticmethod et @classmethod peut être appelée par une instance ou une variable de classe
  • @staticmethod fonction décorée @staticmethod impact sur une sorte de 'propriété immuable' que l'héritage de sous-classe ne peut pas écraser sa fonction de classe de base qui est enveloppée par un décorateur @staticmethod .
  • @classmethod need cls (Nom de la 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 sous-classe, l'héritage de sous-classe peut changer l'effet de la fonction de classe de base, ie la fonction de classe de base enveloppée par @classmethod pourrait être écrasée par différentes sous-classes.

La réponse de Rostyslav Dzinko est très appropriée. J'ai pensé 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é la @classmethod from_string tant from_string pour créer des objets Date partir de paramètres non acceptables. 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

Par conséquent, new_year et millenium_new_year sont des instances de la classe Date .

Mais, si vous observez de près, le processus Factory 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 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 Factory qui est consciente de la classe qui l'a appelée, alors @classmethod est ce dont vous avez besoin.

Réécriture du Date.millenium tant que (c'est la seule partie du code ci-dessus qui change)

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

s'assure que la class n'est pas codée en dur mais plutôt apprise. cls peut être n'importe quelle sous-classe. L' object résultant 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 été utilisé à la place de @staticmethod


On pourrait utiliser @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..."

@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 habituellement 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).


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

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

@classmethod 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, peut être surchargée par la sous-classe. C'est parce que le premier argument de la fonction @classmethod doit toujours être cls (class) .

  • Les 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

here bon lien vers ce sujet.





class-method