En manipulant des classes en Python, vous tomberez sur un décorateur appelé @classmethod. Cela a un objectif unique qui le distingue clairement de @staticmethod ou d'une méthode d'instance (Instance Method).
Dans cet article, nous allons clarifier ce qu'est @classmethod et dans quelles situations il doit être utilisé par rapport à @staticmethod et aux méthodes d'instance.
1. Trois types de méthodes : Instance vs. Class vs. Static
Une classe Python contient fondamentalement trois types de méthodes. La plus grande différence entre elles réside dans ce que la méthode reçoit comme premier argument.
class MyClass:
# 1. Méthode d'instance (Instance Method)
def instance_method(self, arg1):
# 'self' reçoit une instance de la classe.
# Vous pouvez accéder et modifier les propriétés de l'instance (self.x).
print(f"Méthode d'instance appelée avec {self} et {arg1}")
# 2. Méthode de classe (Class Method)
@classmethod
def class_method(cls, arg1):
# 'cls' reçoit la classe elle-même (MyClass).
# Elle est principalement utilisée pour accéder aux propriétés de classe,
# ou jouer le rôle d'un 'factory' pour créer des instances.
print(f"Méthode de classe appelée avec {cls} et {arg1}")
# 3. Méthode statique (Static Method)
@staticmethod
def static_method(arg1):
# Ne reçoit ni 'self' ni 'cls'.
# C'est une fonction utilitaire qui n'est pas liée à l'état de la classe ou de l'instance.
# Cette fonction se contente d'être 'dans' la classe.
print(f"Méthode statique appelée avec {arg1}")
# Exemple d'appel de méthode
obj = MyClass()
# Méthode d'instance (appelée via l'instance)
obj.instance_method("Hello")
# Méthode de classe (peut être appelée via la classe ou l'instance)
MyClass.class_method("World")
obj.class_method("World") # C'est également possible, mais il est plus clair de l'appeler avec la classe.
# Méthode statique (peut être appelée via la classe ou l'instance)
MyClass.static_method("Python")
obj.static_method("Python")
Comparaison rapide
| Catégorie | Premier argument | Cible d'accès | Usage principal |
|---|---|---|---|
| Méthode d'instance | self (instance) |
Propriétés d'instance (self.name) |
Manipuler ou accéder à l'état de l'objet |
| Méthode de classe | cls (classe) |
Propriétés de classe (cls.count) |
Constructeur alternatif (factory) |
| Méthode statique | Aucun | Aucun | Fonction utilitaire logiquement liée à la classe |
2. Utilisation principale de @classmethod : Méthode Factory
Le cas d'utilisation le plus important et général de @classmethod est de définir un "Constructeur alternatif (Alternative Constructor)" ou une "Méthode Factory (Factory Method)".
Le constructeur de base, __init__, reçoit généralement des arguments clairs pour initialiser l'objet. Mais parfois, il est nécessaire de créer des objets d'une autre manière (par exemple, parsing de chaînes, dictionnaires, valeurs de calcul spécifiques).
En utilisant @classmethod, vous pouvez créer un autre 'point d'entrée' qui renvoie une instance de classe en plus de __init__.

Exemple : Créer un objet Person à partir d'une chaîne de date
Supposons que la classe Person reçoit name et age. Si vous souhaitez également créer un objet à partir d'une chaîne au format 'nom-année de naissance', comment procéder ?
import datetime
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def greet(self):
print(f"Bonjour, je suis {self.name} et j'ai {self.age} ans.")
@classmethod
def from_birth_year(cls, name, birth_year):
"""
Reçoit l'année de naissance, calcule l'âge et crée une instance de Person.
"""
current_year = datetime.datetime.now().year
age = current_year - birth_year
# 'cls' fait référence à la classe Person.
# C'est identique à return Person(name, age), mais,
# en utilisant 'cls', on bénéficie d'un atout lors de l'héritage. (voir ci-dessous)
return cls(name, age)
# Utilisation du constructeur de base
person1 = Person("Alice", 30)
person1.greet()
# Utilisation du constructeur alternatif créé avec @classmethod
person2 = Person.from_birth_year("MadHatter", 1995)
person2.greet()
Résultat de sortie :
Bonjour, je suis Alice et j'ai 30 ans.
Bonjour, je suis MadHatter et j'ai 30 ans. (en se basant sur l'année actuelle 2025)
Dans cet exemple, la méthode from_birth_year accède à la classe Person via l'argument cls. Elle utilise ensuite l'âge calculé pour appeler cls(name, age), ce qui est équivalent à appeler Person(name, age) et renvoie une nouvelle instance de Person.
3. @classmethod et héritage
"Pourquoi ne pas simplement appeler Person(...), pourquoi utiliser cls(...) ?"
La véritable puissance de @classmethod se révèle lors de l'héritage. cls fait référence à _la classe même_ sur laquelle la méthode a été appelée.
Supposons que nous créions une classe Student qui hérite de Person.
class Student(Person):
def __init__(self, name, age, major):
super().__init__(name, age) # Appel du __init__ de la classe parente
self.major = major
def greet(self):
# Redéfinir la méthode
print(f"Bonjour, je suis {self.major} majeur {self.name} et j'ai {self.age} ans.")
# Appeler le @classmethod de la classe parente à partir de la classe Student.
student1 = Student.from_birth_year("Jhon", 2005, "Informatique")
# Ah, une erreur TypeError se produit !
# En appelant Student.from_birth_year,
# Person.from_birth_year sera exécuté,
# et à ce moment-là, 'cls' sera 'Student'.
# C'est-à-dire que return cls(name, age) devient return Student(name, age).
# Cependant, __init__ de Student nécessite un argument 'major' supplémentaire.
Le code ci-dessus renvoie une erreur car __init__ de Student nécessite major. Mais l'essentiel est que cls fait référence à Student, pas à Person.
Si from_birth_year était un @staticmethod ou si nous avions codé en dur Person(name, age) à l'intérieur, l'appel à Student.from_birth_year aurait renvoyé un objet Person au lieu d'un objet Student.
@classmethod garantit que, via cls, même lorsque la classe enfant est appelée, cela crée une instance correcte de la classe enfant. (Bien sûr, si la signature de __init__ de la classe enfant change, from_birth_year doit également être redéfini.)
4. Conclusion
Pour résumer @classmethod, voici les points clés :
-
@classmethodprendcls(la classe elle-même) comme premier argument. -
Son principal usage est 'constructeur alternatif (méthode factory)'. Il est utilisé pour fournir diverses manières de créer des objets en plus de
__init__. -
Il est puissant lorsqu'il est utilisé avec l'héritage. Même lorsque appelé depuis une sous-classe,
clsse réfère à la bonne sous-classe et crée une instance de cette classe.
Rappelez-vous que @staticmethod est pour les fonctions utilitaires qui n'ont absolument besoin d'aucun état de classe/instance, @classmethod est pour les méthodes factory nécessitant un accès à la classe elle-même, et les méthodes d'instance sont utilisées pour manipuler l'état de l'objet (self), ce qui permet d'écrire un code beaucoup plus clair et en accord avec les conventions Python.
Aucun commentaire.