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.
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:
- 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.
- 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:
- Nous avons implémenté l'analyse de chaîne de date en un seul endroit et elle est réutilisable maintenant.
- 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).
-
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 classeDate
, tous les enfants auront égalementfrom_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.
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
@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).
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.
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..."
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
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:
- un descripteur de données dans l'espace de noms de classe (comme une propriété)
- données dans l'instance
__dict__
- 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.)
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.
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.
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.
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)