staticmethod en Python : la fonction rangée dans une classe

staticmethod range une fonction ordinaire dans une classe, sans self ni cls. Quand ce rangement se justifie, et le piège du décorateur oublié.
5 min de lecture
Believemy logo

Définition

Une fonction écrite dans une classe reçoit d'habitude l'objet sur lequel elle travaille, via self. Mais certaines fonctions n'ont besoin de rien de tout cela : elles transforment leurs arguments, sans jamais interroger l'objet ni la classe. Leur donner un paramètre qui ne sert jamais serait absurde, c'est ce que @staticmethod permet d'éviter.

@staticmethod est un décorateur qui range une fonction ordinaire dans une classe, sans self ni cls. Voici à quoi cela ressemble sur une fonction de validation :

PYTHON
class Facture:
    TAUX = 0.20

    @staticmethod
    def numero_valide(numero):
        return len(numero) == 8 and numero.isdigit()

Facture.numero_valide("20260819")   # True

L'appel fonctionne aussi bien sur la classe que sur une instance : la fonction ne recevant rien en coulisses, elle reçoit toujours exactement les mêmes arguments, peu importe par où on l'appelle.


Les trois sortes de méthodes

Python propose trois façons d'écrire une méthode, et le choix entre elles tient à une seule question : de quoi cette méthode a-t-elle besoin pour travailler ?

Le tableau ci-dessous résume les trois réponses, et ce que chacune reçoit automatiquement en premier argument.

ÉcriturePremier paramètre reçuÀ choisir quand
Méthode d'instanceself, l'objetLe travail dépend des données de l'objet
classmethodcls, la classeLe travail dépend de la classe, pas de l'objet
@staticmethodRien du toutLe travail ne dépend que des arguments reçus

La méthode statique est la seule des trois dont la signature ne cache rien : ce que vous écrivez entre les parenthèses est exactement ce qu'elle reçoit. Vous pouvez donc la tester isolément, sans construire d'objet ni préparer le moindre __init__.

Bon à savoir

La différence se voit aussi, un peu, côté performance : une méthode d'instance construit à chaque appel un petit objet intermédiaire, la méthode liée, qu'une méthode statique s'épargne.


Quand elle se justifie

Ranger une fonction dans une classe n'est jamais obligatoire. La question à se poser : pourquoi ne pas l'écrire à côté, comme une fonction indépendante ? Le bon cas est celui d'une fonction utilitaire qui appartient au vocabulaire de la classe sans toucher à son état : une validation, une conversion, un calcul que rien d'autre n'utilise. La ranger dans la classe la place là où le prochain lecteur ira la chercher, plutôt que dans le module, parmi vingt fonctions sans rapport.

La classe suivante montre deux méthodes qui s'y côtoient sans se gêner.

PYTHON
class Temperature:
    def __init__(self, celsius):
        self.celsius = celsius

    @staticmethod
    def fahrenheit_vers_celsius(valeur):
        return (valeur - 32) * 5 / 9

    @classmethod
    def depuis_fahrenheit(cls, valeur):
        return cls(cls.fahrenheit_vers_celsius(valeur))

La conversion ne dépend d'aucun objet : c'est une formule, elle reste statique. Le constructeur de remplacement, lui, a besoin de la classe pour fabriquer l'instance, donc il prend cls. Les deux cohabitent bien : chacune reçoit exactement ce dont elle a besoin.


Le piège du décorateur oublié

C'est l'erreur la plus fréquente sur le sujet, et elle se déclenche tard, une fois le code déjà écrit. Sans l'arobase, Python traite la fonction comme une méthode d'instance ordinaire et glisse l'objet en premier argument à chaque appel.

La conséquence dépend alors de la façon dont on appelle la méthode.

PYTHON
class Facture:
    def numero_valide(numero):     # décorateur oublié
        return len(numero) == 8

Facture.numero_valide("20260819")   # passe
facture = Facture()
facture.numero_valide("20260819")

# TypeError: numero_valide() takes 1 positional argument but 2 were given

L'appel depuis la classe passe sans réagir, aucun objet ne s'invitant dans les arguments. C'est l'appel depuis une instance qui échoue, avec une TypeError annonçant un argument de trop qu'on ne voit nulle part dans son propre code : c'est l'instance, transmise en silence. Ce décalage d'exactement un argument entre ce que la méthode déclare et ce qu'elle reçoit est la signature la plus fiable d'un décorateur manquant.


Statique et héritage

Une méthode statique s'hérite comme le reste, et une classe fille peut la remplacer. Mais elle ne reçoit jamais ni self ni cls : si son code nomme une classe en dur, cette classe reste la même, quelle que soit la fille depuis laquelle on l'appelle.

PYTHON
class Produit:
    TAUX = 0.20

    @staticmethod
    def avec_taxe(montant):
        return montant * (1 + Produit.TAUX)   # figé sur Produit

class Livre(Produit):
    TAUX = 0.055

Livre.avec_taxe(100)   # 120.0, alors que 105.5 était attendu

Livre.avec_taxe(100) renvoie 120.0 plutôt que les 105.5 attendus : la méthode ignore que Livre existe, elle ne connaît que Produit, écrit en dur dans son corps. Dès qu'un attribut de classe entre dans le calcul, la méthode n'est plus statique par nature : c'est un classmethod qu'il faut écrire, pour que cls désigne la bonne classe. Repère utile : si le corps d'une méthode statique prononce le nom de sa propre classe, méfiez-vous.


Questions fréquentes

Question

Pourquoi ne pas écrire une simple fonction en dehors de la classe ?

Rien ne l'interdit, et c'est souvent le meilleur choix : une classe encombrée de fonctions sans rapport avec elle devient pénible à lire. Le critère est le vocabulaire du domaine modélisé. Si la fonction n'a de sens que rapportée à la classe, comme valider un numéro de facture, la ranger dedans documente ce lien. Si elle sert ailleurs, elle appartient au module. Une méthode statique que personne n'appelle jamais par le nom de sa classe s'est probablement trompée d'endroit.

Question

Quelle différence exacte avec classmethod ?

Une méthode de classe reçoit la classe en premier argument, une méthode statique ne reçoit rien. La conséquence est concrète : la première suit la classe réellement appelée, même depuis une fille, la seconde reste figée sur ce que son code nomme explicitement, comme avec Livre et Produit. Dès qu'une classe fille doit changer le résultat, la version statique devient un piège silencieux.

Question

Peut-on l'appeler depuis le corps de la classe ?

Depuis Python 3.10, oui : l'objet produit par le décorateur est devenu appelable directement, ce qui n'était pas le cas avant. Sur les versions antérieures, la même écriture lève un message déroutant, 'staticmethod' object is not callable, et impose de passer par .__func__ pour récupérer la fonction d'origine.

Termes connexes

Découvrez notre glossaire Python

Parcourez les termes et définitions les plus couramment utilisés dans le domaine du développement avec Python.

Partager cet article

Tu veux nous aider ? Fais un lien vers cet article sur tes réseaux ou encore mieux : sur ton site, dans un article ou dans ta newsletter.