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.
Autant que je sache, @classmethod
indique à une classe que c'est une méthode qui devrait être héritée dans des 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
ni aucune définition de @
?
tl; dr: quand devrais-je les utiliser, pourquoi dois-je les utiliser et comment dois-je les utiliser?
Je suis assez avancé en C++, donc utiliser des 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 entre les deux entités: classmethod
doit avoir une référence à un objet de classe comme premier paramètre, tandis que staticmethod
peut avoir aucun paramètre du tout.
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')
Supposons un exemple de classe traitant des informations de date (ce sera notre standard):
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 information de fuseau horaire; supposons que toutes les dates soient présentées en UTC).
Ici nous avons __init__
, un initialiseur typique de Python instances de classe, qui reçoit les arguments comme un 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 certaines tâches qui peuvent être bien effectuées en utilisant classmethod
s.
Supposons que nous voulions créer un grand nombre d'instances de classe Date
dont les informations de date proviennent d'une source externe codée sous forme de chaîne au format 'jj-mm-aaaa'. Supposons que nous devions le faire à différents endroits du code source de notre projet.
Donc, ce que nous devons faire ici est:
Date
en transmettant ces valeurs à l'appel d'initialisation.Cela ressemblera à:
day, month, year = map(int, string_date.split('-'))
date1 = Date(day, month, year)
Pour cela, C++ peut implémenter une telle fonctionnalité avec surcharge, mais Python ne possède 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')
Examinons plus attentivement l'implémentation ci-dessus et examinons les avantages que nous avons ici:
cls
est un objet qui contient le la classe elle-même, pas une instance de la classe. C'est plutôt cool car si nous héritons de notre classe Date
, tous les enfants auront également from_string
défini.Méthode statique
Qu'en est-il de staticmethod
? C'est assez similaire à classmethod
mais ne prend aucun paramètre obligatoire (comme le font les méthodes class ou d'instance).
Regardons le cas d'utilisation suivant.
Nous avons une chaîne de date que nous voulons valider d'une manière ou d'une autre. Cette tâche est également liée de manière logique à la classe Date
que nous avons utilisée jusqu'à présent, mais ne nécessite pas d'instanciation de celle-ci.
Voici où staticmethod
peut être utile. Regardons le morceau de code suivant:
@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 on peut le voir avec l'utilisation de staticmethod
, nous n'avons aucun accès à ce que la classe est --- c'est en fait juste une fonction, appelée syntaxiquement comme une méthode, mais sans accès à l'objet et à ses les internes (champs et autres méthodes), alors que classmethod le fait.
La réponse de Rostyslav Dzinko est très appropriée. Je pensais pouvoir mettre en évidence une autre raison pour laquelle vous devriez choisir @classmethod
par rapport à @staticmethod
lorsque vous créez un constructeur supplémentaire.
Dans l'exemple ci-dessus, Rostyslav a utilisé le @classmethod
from_string
en tant qu'usine pour créer des objets Date
à partir de paramètres inacceptables. La même chose peut être faite avec @staticmethod
comme le montre 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, 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 en soit. Cela signifie que même si la classe Date
est sous-classe, les sous-classes continueront de créer 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 @staticmethod
décorateur utilisé.
Dans la plupart des cas, cela est indésirable. Si vous voulez une méthode Factory qui connaisse la classe qui l'a appelée, alors @classmethod
est ce dont vous avez besoin.
Réécriture du Date.millenium
en 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 le class
n'est pas codé en dur mais plutôt appris. cls
peut être n'importe quelle sous-classe. Le object
résultant sera à juste titre une instance de cls
. Testons 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 que, comme vous le savez maintenant, @classmethod
a été utilisé à la place de @staticmethod
@classmethod
signifie: lorsque cette méthode est appelée, nous passons la classe comme premier argument à la place de l'instance de cette classe (comme nous le faisons normalement avec des méthodes). Cela signifie que vous pouvez utiliser la classe et ses propriétés dans cette méthode plutôt que dans une instance particulière.
@staticmethod
signifie: lorsque cette méthode est appelée, nous ne lui passons pas une instance de la classe (comme nous le faisons normalement avec des méthodes). Cela signifie que vous pouvez insérer une fonction dans une classe, mais que vous ne pouvez pas accéder à l'instance de cette classe (utile lorsque votre méthode n'utilise pas l'instance).
@staticmethod
La fonction n'est rien de plus qu'une fonction définie dans une classe. Il est appelable sans instancier d'abord la classe. Sa définition est immuable par héritage.
La fonction @classmethod
est également appelable sans instancier la classe, mais sa définition suit la classe Sub, et non la classe Parent, via l'héritage, pouvant être remplacée par la sous-classe. C’est parce que le premier argument de la fonction @classmethod
doit toujours être cls (class)
.
ici est un bon lien vers ce sujet.
Signification de
@classmethod
et@staticmethod
?
self
) en tant que premier argument implicite.cls
) en tant que premier argument implicite.quand devrais-je les utiliser, pourquoi devrais-je les utiliser et comment les utiliser?
Vous n'avez pas besoin ni de décorateur. Mais sur le principe que vous devez minimiser le nombre d’arguments des fonctions (voir Clean Coder), ils sont utiles pour 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éfinissez some_function
', par exemple:
some_function_h = some_function_g = some_function_f = lambda x=None: x
et cela fonctionnera.)
Une recherche en pointillés sur une instance est effectuée dans cet ordre - nous recherchons:
__dict__
Remarque, une recherche en pointillé sur une instance est appelée comme ceci:
instance = Example()
instance.regular_instance_method
et les méthodes sont des attributs appelables:
instance.regular_instance_method()
L'argument, self
, est donné implicitement via la recherche en pointillé.
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>
L'argument, cls
, est donné implicitement via une recherche en pointillé.
Vous pouvez accéder à cette méthode via une instance ou la classe (ou sous-classes).
>>> instance.a_class_method()
<class '__main__.Example'>
>>> Example.a_class_method()
<class '__main__.Example'>
Aucun argument n'est implicitement donné. Cette méthode fonctionne comme n'importe quelle fonction définie (par exemple) sur un espace de noms de modules, 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?
Chacune de ces méthodes est progressivement plus restrictive dans les informations transmises à la méthode par rapport à la méthode par instance.
Utilisez-les lorsque vous n'avez pas besoin de l'information.
Cela rend vos fonctions et méthodes plus faciles à raisonner et à unifier.
Quel est le plus facile de 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.
Celles-ci s'apparentent à des méthodes d'instance, de classe et statique. Gardant à l'esprit que lorsque nous avons un exemple, nous avons aussi sa classe, encore une fois, demandez-vous quelle est la 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):
...
Voici quelques exemples classiques que je préfère:
La méthode statique str.maketrans
était une fonction du module string
, mais il est beaucoup plus pratique qu'elle soit accessible à partir de l'espace de nom 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 récupère 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'>
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 se trouve 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 son autre classe ou ses méthodes statiques, ou peut-être en tant que constructeur. (Vous ne coderiez pas la classe de manière à ce que les sous-classes puissent être utilisées ici.)
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.
En utilisant statique, vous voudriez que le comportement reste inchangé dans toutes 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..."
Une petite compilation
@ staticmethod Un moyen d'écrire une méthode dans une classe sans faire référence à l'objet sur lequel elle est appelée. Donc pas besoin de passer des arguments implicites comme self ou cls. Elle est écrite exactement de la même manière que celle utilisée en dehors de la classe, mais elle n’a aucune utilité dans python, car si vous devez encapsuler une méthode dans une classe, celle-ci doit faire partie de cette classe @ staticmethod est pratique dans ce cas.
@ classmethod Il est important lorsque vous voulez écrire une méthode factory et que cet attribut personnalisé peut être attaché à une classe. Cet attribut peut être remplacé dans la classe héritée.
Une comparaison entre ces deux méthodes peut être comme ci-dessous
En bref, @classmethod transforme une méthode normale en une 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 une méthode @ class, vous devriez vous efforcer de créer des instances une par une et elles sont dispersées.
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 rassemblées.
En conclusion, @classmethod decorator convertit une méthode conventionnelle en une méthode d'usine. Utiliser classmethods permet d'ajouter autant de constructeurs alternatifs que nécessaire.
@ classmethod
@classmethod
peut être comparé à __init__
. Vous pourriez penser que c'est une autre __init__()
. C’est ainsi que python réalise la surcharge du constructeur de classe en c ++.
class C:
def __init__(self, parameters):
....
@classmethod
def construct_from_func(cls, parameters):
....
obj1 = C(parameters)
obj2 = C.construct_from_func(parameters)
remarquez qu'ils ont tous deux une référence pour class comme premier argument de définition alors que __init__
utilise self
mais construct_from_func
utilise cls
de manière conventionnelle.
@ staticmethod
@staticmethod
peut être comparé à object method
class C:
def __init__(self):
....
@staticmethod
def static_method(args):
....
def normal_method(parameters):
....
result = C.static_method(parameters)
result = obj.normal_method(parameters)
Je suis débutant sur ce site, j'ai lu toutes les réponses ci-dessus et j'ai obtenu ce que je voulais. Cependant, je n'ai pas le droit de faire un vote par appel nominal. Je veux donc commencer à StackOverflow avec la réponse telle que je la comprends.
@staticmethod
n'a pas besoin de self ou de cls en tant que premier paramètre de la méthode@staticmethod
et @classmethod
fonction encapsulée peut être appelée par instance ou variable de classe@staticmethod
a un impact sur une sorte de 'propriété immuable' que l'héritage de la sous-classe ne peut pas écraser sa fonction de classe de base qui est encapsulé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é de manière sous-classe, l'héritage de sous-classe peut changer l'effet de la fonction de classe de base, c'est-à-dire @classmethod
la fonction de classe de base encapsulée pourrait être écrasée par différentes sous-classes.Une façon de penser légèrement différente qui pourrait être utile à quelqu'un ... Une méthode de classe est utilisée dans une super-classe pour définir le comportement de cette méthode lorsqu'elle est appelée par différentes classes enfant. Une méthode statique est utilisée lorsque nous voulons renvoyer la même chose quelle que soit la classe enfant que nous appelons.