Python et les objets statiques

publicité
Python et les objets statiques
Les variables statiques
En C/C++, il est l’usage d’avoir des variables statiques, c’est-à-dire non-dynamiques, pour le
comptage d’objets ou pour des flags en code embarqué, ou l’on évite comme la peste les new et
delete. C’est intégré au langage, avec le mot clef ‘static’, au cas où la définition globale - hors des
accolades - n’y suffit pas. Une variable statique est aussi privée au module, afin de réduire sa
visibilité.
Déclarer une variable static revient à lui donner une adresse mémoire définitive. Au niveau du
module, on le comprend aisément. C’est plus surprenant dans une fonction : on la retrouve
inchangée depuis le dernier appel – a moins bien sûr que la fonction en question ne la modifie ! Cette
persistance est valable même si elle est déclarée à l’intérieur d’un objet alloué dynamiquement. Mais
en Python, comment reproduire ce comportement ?
Petite digression : dans ce texte, j’utilise souvent le mot fonction par habitude, alors que la
terminologie Python est : method.
A priori, « static » va l’encontre de la programmation orientée objet… Mais parfois, on aimerait un
objet unique, a dispo de manière globale, déclaré et initialisé au niveau du module. Il serait ‘objet’
pour la beauté de la pensée, donc de la programmation, plutôt qu’un fatras de variables globales ;
même si elles sont utilisées au niveau du module. Cet objet serai statique parce que l’instancier ne
fait que de compliquer le code, voire de prendre le risque de dédoubler l’objet par mégarde alors
qu’il doit être unique.
C’est un peu tordu : si un objet n’est pas instancié… il doit être un peu instancié quand même,
puisque par définition, une variable statique doit exister au moment où le programme tourne. Les
exemples de codes sont entrés au niveau de la console Python ; ici en version 2.7.2, dans
l’environnement (gratuit) PyCharm Community : http://www.jetbrains.com/pycharm/
Variable de classe statique
Bien. Mais comment, en Python, reproduire ce qu’on sait si bien faire en C/C++ ?
Python permet la définition de variables statiques tout court si, hors d’une classe, définie au niveau
du module; et dans un objet, si défini hors des fonctions de la classe. On s’intéresse à cette
possibilité. Dans un tel cas, elle est accédée par le nom de la classe.
A priori, ça fonctionne fort bien ! En utilisant la classe de base, soit Toto - avec la majuscule qui
symbolise la déclaration de classe - la variable ‘a’ d’instance est accessible et modifiable. C’est
pratique, et on peut toujours instancier la classe normalement, p. exemple dans la variable x :
Ha. Ça donne 4, on attendait un 6. La variable ‘a’ de la classe de base reste donc inchangée ; de plus
c’est cette valeur (donc : 4) qui serait utilisée à la prochaine instanciation (par exemple : y = Toto() ).
On peut donc compter des objets, par exemple. Par contre, dès qu’on touche à la variable d’une
instance, celle-ci devient indépendante et suit la dynamique de l’instance.
En prime, on peut toujours ajouter une variable au vol à la classe de base :
On la retrouve dans les objets précédemment instanciés, qui se rabattent sur la classe de base pour
l’afficher. Bien entendu, on peut modifier l’instance x ou y, sans que cela touche la classe de base,
pour à nouveau, avoir une variable d’instance propre et indépendante.
Les fonctions et le statique (@staticmethod)
Et les fonctions ? Ça ne marche pas si bien :
On beau essayer de passer la classe de base par Toto.prn(Toto), seule une version instanciée de Toto
fonctionne, comme le montre l’essai avec x. Sinon, on obtient l’erreur « unbound method prn() … »
Donc, on doit avoir une instance pour que la fonction… fonctionne. On peut toutefois forcer Python à
intégrer la fonction dans la classe de base. L’astuce est de la précéder de la ligne : @classmethod. La
fonction reçoit en paramètre classiquement sa propre self-référence. Et là :
On y arrive ! OK pour le statique. Avec une instance, ça se gâte :
On doit donner comme paramètre d’appel de fonction le nom de l’instance, alors qu’avec une classe
« classique », si j’ose dire, c’est implicite. Pas terrible comme écriture de code, un peu verbeux à mon
goût. Autre solution, c’est de virer l’argument, et de coder :
def prn() :
print Toto.a
On en revient à une méthode de module, écrite hors de la définition de classe…
Fonction de classe de base (@classmethod)
Un autre décorateur permet de lier la fonction à la classe
Elle reçoit comme 1er paramètre non pas l’instance, mais la classe de base. Elle convient parfaitement
pour compter les objets. L’écriture du code est plus claire.
Cependant, une variable instanciée ne sera pas à sa portée, et peut pousser à la faute… On ne peut
pas tout avoir.
En résumé
Pour obtenir un objet statique en Python, il faut :


mettre les variables après la déclaration de la classe, avant les méthodes.
les méthodes doivent être précédées du décorateur @classmethod
Précaution : ne pas instancier cet objet dynamiquement. On peut carrément afficher un message
d’erreur avec la fonction __init__(), pour en informer l’utilisateur.
Yves Masur (1/2014)
Références
http://fr.wikibooks.org/wiki/Programmation_C/Classe_de_stockage
http://stackoverflow.com/questions/68645/static-class-variables-in-python
http://stackoverflow.com/questions/735975/static-methods-in-python
http://docs.python.org/2/library/functions.html?highlight=decorator
Téléchargement