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 d'instance) reçoit l'instance (que nous appelons généralement
self
) en tant que premier argument implicite.
- Une méthode de classe reçoit la classe (que nous appelons généralement
cls
) en tant que premier argument implicite.
- Une méthode statique n'a pas d'argument implicite (comme une fonction régulière).
quand devrais-je les utiliser, pourquoi devrais-je les utiliser et comment devrais-je les utiliser?
Vous n'avez pas besoin de ces décorateurs. Mais selon le principe que vous devriez 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):
"""Une fonction d'une instance a accès à chaque attribut de cette instance, y compris sa classe (et ses attributs).
Ne pas accepter au moins un argument est une TypeError.
Ne pas comprendre la sémantique de cet argument est une erreur de l'utilisateur.
"""
return some_function_f(self)
@classmethod
def a_class_method(cls):
"""Une fonction d'une classe a accès à chaque attribut de la classe.
Ne pas accepter au moins un argument est une TypeError.
Ne pas comprendre la sémantique de cet argument est une erreur de l'utilisateur.
"""
return some_function_g(cls)
@staticmethod
def a_static_method():
"""Une méthode statique n'a aucune information sur les instances ou les classes
à moins qu'elles ne soient explicitement données. Elle existe simplement dans l'espace de noms de la classe (et donc de ses instances).
"""
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 TypeError, mais ne pas comprendre la sémantique de cet argument est une erreur de l'utilisateur.
(Définissez les fonctions some_function
, par exemple:
some_function_h = some_function_g = some_function_f = lambda x=None: x
et cela fonctionnera.)
consultations par points sur les instances et les classes :
Une consultation par point sur une instance se fait dans cet ordre - nous cherchons:
- un descripteur de données dans l'espace de noms de la classe (comme une propriété)
- des données dans l'instance
__dict__
- un descripteur non-données dans l'espace de noms de la classe (méthodes)
Remarquez, une consultation par point sur une instance est invoquée comme suit :
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 consultation par point.
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 la consultation par point.
Vous pouvez accéder à cette méthode via une instance ou la classe (ou les sous-classes).
>>> instance.a_class_method()
>>> Example.a_class_method()
méthodes statiques
Aucun argument n'est implicitement donné. Cette méthode fonctionne comme n'importe quelle fonction définie (par exemple) dans l'espace de noms de modules, sauf qu'elle peut être consultée
>>> print(instance.a_static_method())
None
Encore une fois, quand devrais-je les utiliser, pourquoi devrais-je les utiliser?
Chacun de ces éléments transmet progressivement moins d'informations à la méthode par rapport aux méthodes d'instance.
Utilisez-les lorsque vous n'avez pas besoin de l'information.
Cela rend vos fonctions et méthodes plus faciles à comprendre et à tester unitairement.
Laquelle est plus facile à comprendre?
def fonction(x, y, z): ...
ou
def fonction(y, z): ...
ou
def fonction(z): ...
Les fonctions avec moins d'arguments sont plus faciles à comprendre. Elles sont également plus faciles à tester unitairement.
Cela ressemble aux méthodes d'instance, de classe et statiques. En gardant à l'esprit que lorsque nous avons une instance, nous avons également sa classe, interrogez-vous de nouveau, laquelle est plus facile à comprendre?:
def une_methode_d_instance(self, arg, kwarg=None):
cls = type(self) # A également la classe de l'instance!
...
@classmethod
def une_methode_de_classe(cls, arg, kwarg=None):
...
@staticmethod
def une_methode_statique(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 dans le module string
, mais il est beaucoup plus pratique qu'elle soit accessible depuis l'espace de noms de str
.
>>> 'abc'.translate(str.maketrans({'a': 'b'}))
'bbc'
La méthode de classe dict.fromkeys
retourne 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-classe, nous voyons qu'elle 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'))
Mon conseil - Conclusion
Utilisez les 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 soit dans l'espace de noms de l'objet.
Utilisez les méthodes de classe lorsque vous n'avez pas besoin d'informations sur l'instance, mais avez besoin des informations sur la classe peut-être pour ses autres méthodes de classe ou statiques, ou peut-être pour lui-même en tant que constructeur. (Vous ne voudriez pas coder en dur la classe pour que des sous-classes puissent être utilisées ici.)