IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Initiation à Python


précédentsommairesuivant

V. Modulariser

V-A. Regrouper dans des fonctions

À l'heure actuelle, nos programmes sont composés de petites suites d'opérations qui s'enchaînent les unes à la suite des autres dans le même ordre. Nous avons vu que pour répéter une opération nous pouvions utiliser des boucles. Mais cela ne change rien au fait que l'enchaînement est parfaitement continu. Or, il est souvent nécessaire de répondre à une action de l'utilisateur effectuée à un moment donné, parmi d'autres. Chaque action faisant appel à un bloc d'instruction, chaque action pouvant être déclenchée par l'utilisateur à certains moments ou à tous moments, il est important d'avoir une conception des instructions qui ne soient pas complètement linéaires. Pour cela, nous utilisons des fonctions : ce sont des blocs d'instructions auxquels nous avons attribué un nom de façon à pouvoir les appeler, les déclencher au moment opportun. Cela prend la forme suivante :

 
Sélectionnez
def nomdelafonction():
    bloc d'instruction

Il s'agit là de la portion de Python qui définit ou déclare la fonction, un peu comme on déclare une variable. Elle commence par le mot clé def suivi du nom de la fonction (selon les mêmes règles de nommage que les variables d'environnement), suivie elle-même de parenthèses qui permettent de la différencier de simples variables (et nous verrons qu'elles pourront être autrement utiles) et d'un : qui mentionne le début d'un bloc. Le bloc qui suit doit être indenté et toutes les lignes qui la composent indentées de façon similaire, comme dans le cas des boucles.
Pour appeler la fonction, nous noterons simplement son nom sous la forme :

 
Sélectionnez
nomdelafonction()

un appel de fonction peut être réalisé à n'importe quel moment du programme, y compris à partir d'une boucle, voire d'une autre fonction.

V-B. Rendre les fonctions plus universelles avec des paramètres

L'avantage d'une fonction est de pouvoir être réutilisée dans différents contextes. Mais en imaginant une fonction qui affiche un texte du type :

 
Sélectionnez
def affiche():
    print "Bonjour"

que faire si nous souhaitons aussi afficher « bonsoir » ? Faut-il créer une autre fonction ? Et ainsi les cumuler au point de s'y perdre. Il semble évident que non. Python, comme tous les langages modernes, permet l'utilisation de paramètres. Ces paramètres sont des valeurs associées à des variables qui seront transmises directement à la fonction.

Les paramètres sont en général déclarés dans la déclaration de définition et remplacent les valeurs constantes dans le bloc. On obtient ainsi :

 
Sélectionnez
def affiche(mot):
    print mot

et pour appeler la fonction :

 
Sélectionnez
affiche("bonjour")

> bonjour

 
Sélectionnez
affiche("bonsoir")

> bonsoir

Si plusieurs paramètres sont nécessaires, il suffit de les séparer par des virgules dans la déclaration et dans l'appel :

 
Sélectionnez
def affiche(mot, nom):
    print mot + ', ' +nom

affiche("Bonjour", "Richard")

ce qui est encore plus intéressant en jouant avec des valeurs inconnues saisies par l'utilisateur, récupérées dans des sources de données ou autre :

 
Sélectionnez
def affiche(mot,nom):
    print mot+', '+nom

print "Quel mot afficher ? "
mot = raw_input()
print "pour qui ?"
nom = raw_input()
affiche(mot,nom)

enregistré dans un fichier et lancé :

 
Sélectionnez
> Quel mot afficher ?
Bonjour
> pour qui ?
Richard
> Bonjour, Richard

Attention cependant c'est l'ordre des paramètres qui compte, pas leur nombre :

 
Sélectionnez
affiche(nom,mot)

> Richard, Bonjour

V-C. Paramètres inconnus

Lorsque le nombre des paramètres est inconnu, par exemple lorsqu'ils sont issus d'une liste, nous sommes face à un problème : il n'est pas toujours possible de les déclarer ou de les appeler.

De fait, nous avons deux possibilités qui répondent à des besoins différents. Si nous connaissons le nombre de paramètres mais ne savons pas s'il sera toujours possible de leur affecter une valeur, ou si nous ignorons complètement la nature et le nombre des paramètres.

Dans le premier cas nous pourrons attribuer des valeurs par défaut :

 
Sélectionnez
def (nom, mot="Bonjour"):
    print mot+', '+nom

affiche("Richard")

> Bonjour, Richard

 
Sélectionnez
affiche("Richard","Bonsoir")

> Bonsoir, Richard

voire de façon plus explicite pour éviter tout risque de confusion en particulier si le nombre de paramètres :

 
Sélectionnez
affiche("Richard", mot="Bonsoir")

Dans le second cas, nous placerons une étoile (liste) ou deux (dictionnaires) devant un paramètre référent :

 
Sélectionnez
liste1 = [5,10,3,7]
def moyenne(*num):
    print "La moyenne est de "+sum(*num)/len(*num)

moyenne(liste2)

> La moyenne est de 6

V-D. Réutiliser le résultat d'une fonction

Le bloc d'instruction va effectuer une ou plusieurs actions. le résultat de ses actions ne peut être utilisé qu'en interne ou avoir besoin de resservir dans une autre partie du programme. Imaginons par exemple un jeu dans lequel nous devons mémoriser un score suite à des choix opportuns ou non. Chaque choix déclenche l'ajout ou la suppression de point, et le total doit être réutilisé dans les actions suivantes. Nous pourrions dans ce cas avoir une fonction par action et une fonction pour le calcul des points. Les actions envoient leur résultat à la fonction calculant les points pour effectuer le calcul adéquat. Le total peut lui-même être utilisé pour donner des bonus ou faire des changements de niveau.

Chaque total peut donc être enregistré dans une variable globale ou renvoyé à la fonction qui l'appelle afin qu'elle en tienne compte dans le déroulé des actions du jeu.

Un cas simple : écrire un résultat provenant d'un calcul utilisé dans une fonction.
L'exemple de base pourrait être :

 
Sélectionnez
def calcul(param1):
    param2=10
    print "le résultat est "+ str(param1+param2)

calcul(5)

> le résultat est 15

on pourrait remplacer cet exemple par

 
Sélectionnez
def calcul():
    return param1+param2

print "le résultat est "+ str(calcul(5))

> le résultat est 15

L'avantage de cette méthode est d'offrir une meilleure réutilisation de la fonction. Dans le premier cas, la fonction calcule et affiche le résultat elle-même, ce qui la rend inexploitable dans le cas où le calcul seul doit être effectué.

Dans le second cas, la fonction ne fait que le calcul et laisse le soin au contexte de savoir qu'en faire.

V-E. Faciliter la réutilisation avec les classes

De nombreuses personnes utilisent Python sans utiliser de classes et pourtant l'un des premiers arguments que l'on avance en présentant Python est son orientation 100 % objet. Il pourra peut-être vous sembler étrange de se passer d'une telle fonction du langage si elle est tellement centrale, voire comment certains font pour s'en passer.

C'est une question que je me pose régulièrement : pourquoi se passer de la programmation objet alors qu'elle est si simple, si pratique et offre finalement et sans effort de nombreuses possibilités.

Moi qui écris ces lignes et qui ne suis que simple graphiste ou artiste et loin d'être un programmeur hors-pair comme d'autres peuvent s'en vanter, voilà pourtant que, si je devais faire des statistiques de mon code produit, l'utilisation de la programmation objet représenterait au moins 90 % des lignes écrites et surtout conservées. J'espère que vous n'en conclurez pas que, comme je ne suis pas un expert, je me trompe. Si vous avez des doutes, il vous suffira de faire une petite recherche sur Internet et vous vous rendrez rapidement compte que les discours sont en faveur de la programmation objet.

Finalement, le point à retenir est que si je peux le faire, vous le pouvez, qui que vous soyez.
La programmation orientée objet est une forme supplémentaire d'organisation du code produit. Jusqu'ici, nous avons utilisé des blocs d'instructions, puis des fonctions, en passant par des variables. Le plus évolué en termes de structuration est sans conteste, jusqu'à présent, l'utilisation de fonctions. Cependant, nous avons des petits soucis avec ces fonctions. Par exemple, si nous programmons un site Internet, nous avons besoin d'une fonction qui vérifie si un utilisateur est connecté et une autre qui liste les fichiers d'un répertoire. A priori, ces deux fonctions n'ont rien à voir ensemble et elles seront cependant accessibles de la même façon, au même niveau. Un peu comme si vous rangiez les torchons avec les serviettes (je sais vous le faites peut-être et moi aussi, mais ce n'est pas une excuse).
En programmation objet, nous allons ajouter au-dessus de la fonction habituelle def un élément qui va permette de classer les fonctions selon leur contexte d'utilisation. Parti de ce principe simple, beaucoup de choses vont alors devenir possibles.

V-F. Définir des classes

Définir une classe se fait simplement en utilisant le mot réservé class suivi du nom personnalisé de la classe en tant que début de bloc d'instruction :

 
Sélectionnez
class MaClasse :
    #instructions

En ce qui concerne le nom de la classe, il est de coutume de respecter toujours la même règle, par simplicité. Nous utilisons ici un nom composé sans séparation entre les termes mais avec une majuscule au début de chacun d'entre eux.
Une fois la classe créée, il va être possible de l'instancier, c'est-à-dire de l'utiliser dans un cas concret que l'on pourra appeler objet, tout comme chaque table est une instance de ce que peut être une table.
L'instanciation se fait simplement en faisant une affectation :

 
Sélectionnez
objet = MaClasse()

Malheureusement, si vous essayez ces quelques lignes dans l'interpréteur Python, vous n'obtiendrez pas grand-chose :

 
Sélectionnez
class MaClasse :
     #instructions
 
Sélectionnez
File "<stdin>", line 3
          ^
IndentationError: expected an indented block

Le message n'est pas très explicite, mais il mentionne bien qu'il manque quelque chose à cette classe : nous devrons donc lui ajouter des méthodes et des attributs. 

V-G. Ajouter des attributs

À quoi sert de créer des classes si elles ne contiennent rien ? A priori à rien. La première tâche fréquente dans la définition des classes est souvent la définition de variables, comme pour toute autre structure. On appellera alors la variable « attribut » car elle devient une propriété liée en propre à l'objet et n'existe que dans son contexte.

 
Sélectionnez
class MaClasse :
     essai = "ok"

Notez au passage l'indentation, car la classe agit comme un bloc d'instruction structuré. Il faudra donc être vigilant à ce niveau.
Essayons maintenant de l'instancier.

 
Sélectionnez
objet = MaClasse()
print objet.essai

Affiche : ok

Remarquez lors de l'instanciation, des parenthèses sont ajoutées au nom de la classe et aussi que pour faire référence à une propriété, il faut que :

  • celle-ci soit définie dans la classe ;
  • l'objet qui la concerne soit instancié ;
  • la rattacher à l'instance en plaçant un point entre les deux.

pour vérifier le type il est toujours possible d'utiliser la méthode type :

 
Sélectionnez
type(MaClasse)

<type 'classobj'>

Ainsi, l'attribut essai se retrouve associée à l'instance et il peut y avoir autant d'attributs du même type qu'il y a d'instance :

 
Sélectionnez
class MaClasse :
       essai = "ok"

objet = MaClasse()
print objet.essai

> ok

 
Sélectionnez
objet2 = MaClasse()
print objet2.essai

> ok

 
Sélectionnez
objet2.essai = "wow"
print objet2.essai,  objet.essai

> wow ok

Nous utiliserons régulièrement le signe > en début de ligne pour montrer ce que l'interpréteur renvoie. Cette pratique est inverse à l'interpréteur Python mais nous permet, nous l'espérons, rendre plus claire les lignes de codes qui nous intéressent.

On voit bien dans cet exemple que la valeur de l'attribut a pu être modifiée dans le bloc principal avec une simple affectation et que pourtant, l'affectation ayant été effectuée dans le contexte d'une instance, seule la valeur associée à l'attribut de cette instance a été réellement affectée, l'autre instance n'ayant pas été modifiée le moins du monde.

V-H. Ajouter des méthodes

Il en est des fonctions comme des variables. Dans le cadre d'une classe, elles deviennent des « méthodes ». Cette différence de terme peut sembler capricieuse, elle permet cependant de toujours bien savoir à quel niveau on parle.

V-H-1. Des méthodes standards

Les méthodes vont permettre de définir des regroupements de fonctions propres à notre contexte de manière à mieux les exploiter, en respectant systématiquement ce contexte.

La définition d'une fonction interne à une classe est très simple, puisqu'elle ne contient aucune différence avec la façon standard de la définir : on utilise le mot clé def suivi du nom de la fonction, un ":", puis à la ligne le bloc d'instruction indenté.

 
Sélectionnez
class MaClasse:
     def affiche(self):
             essai="ok"
             return essai

objet = MaClasse()
print objet.affiche()

> ok

V-H-2. _init__ et self, une méthode particulière

__init__ est une méthode particulière qui est lancée automatiquement lors de la déclaration d'une instance. On l'appelle « constructeur » parce qu'elle aide à poser les bases de toute l'instance. On y déclare en général les principaux attributs et on peut éventuellement y commencer du traitement par appel de certaines méthodes.

Dans l'exemple qui précède, l'utilisation du constructeur pourrait conduire à un programme comme celui-ci :

 
Sélectionnez
class MaClasse:
    
     def __init__(self):
             self.essai="ok"

     def affiche(self):
             return self.essai

objet = MaClasse()
print objet.affiche()

> ok

Le constructeur contient en général au minimal l'affectation de base des premières variables les plus importantes du programme de manière à s'assurer qu'elles sont bien déclarées et seront parfaitement partagées par la suite au sein des différentes méthodes.

Le mot-clé self permet quant à lui, de spécifier que l'action s'effectue bien au niveau de l'instance elle-même. Nous aborderons plus abondamment ce sujet en parlant ultérieurement de leur opposé, les attributs de classe.

Enfin, si __init__ est un constructeur, __del__ est un destructeur. Plus rarement utilisé en Python puisque l'interpréteur prend en charge la destruction des objets lorsqu'ils deviennent inutilisés, le destructeur permet cependant de mieux contrôler les événements et de déclencher éventuellement des actions à la destruction d'une instance, comme renvoyer un message, par exemple.

 
Sélectionnez
class MaClasse:
     def __init__(self):
             self.essai="ok"
     def affiche(self):
             return self.essai
     def __del__(self):
             print "objet détruit"
 
objet = MaClasse()
print objet.affiche()

> ok

 
Sélectionnez
objet.__del__()

> objet détruit

Ici, nous forçons la destruction de l'objet pour l'exemple, mais cette action est bien sûr réalisable dans tout projet, même si elle n'est pas nécessaire.

V-I. Manipulations d'attributs

V-I-1. Manipuler les attributs

Jusqu'ici, nous avons observé que les attributs auront des valeurs redéfinissables pour chaque objet. Mais qu'en est-il si l'on souhaite avoir une valeur partagée entre plusieurs instances ou à l'inverse gagner un peu en définissant spécifiquement les valeurs pour une instance.

V-I-2. Attribut de classe

Nous avons manqué un peu de précision en définissant précédemment les attributs. Nous avons présenté notre attribut essai de la même façon alors qu'il est apparu dans deux contextes différents : celui de la racine de la classe et celui de la méthode. Nous avons mentionné que cet attribut était lié à l'instance, ce qui n'est pas tout à fait juste. Prenons le point suivi pour aborder le sujet : si à l'inverse, nous souhaitions que la même valeur soit partagée par plusieurs instances, la syntaxe de l'attribut devrait être un peu différente : on le définira en début de classe en dehors de toute méthode ou on le préfixera avec le nom de la classe de manière que l'attribut y soit explicitement associé, et non pas aux instances :

 
Sélectionnez
class MaClasse :
      essai = "ok"
      def affiche(self):
                print essai

objet = MaClasse()
objet2 = MaClasse()
objet.affiche()
 
Sélectionnez
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in affiche
NameError: global name 'essai' is not defined

On voit dans cet exemple qu'un petit problème se pose : il n'est pas possible d'accéder à l'attribut essai à partir de la méthode affiche. La première solution qui pourrait nous venir à l'esprit serait d'utiliser le mot clé self pour spécifier l'appartenance de l'attribut, mais voici :

 
Sélectionnez
class MaClasse :
     essai = "ok"
     def affiche(self):
             print self.essai

objet = MaClasse()
objet.affiche()

> ok

 
Sélectionnez
objet2 = MaClasse()
objet2.affiche()

> ok

 
Sélectionnez
objet2.essai = "wow"
objet2.affiche()

> wow

 
Sélectionnez
objet.affiche()

> ok

Nous nous retrouvons alors dans la même configuration que précédemment et ne résolvons pas notre problème : l'attribut est associé à l'instance et n'est pas partagé avec celle-ci.
La solution consiste à associer explicitement l'attribut à la classe en le pointant comme tel. Au lieu d'utiliser le mot-clé self, nous utiliserons alors le nom de la classe pour bien préciser que c'est à son niveau que les opérations vont s'effectuer.

 
Sélectionnez
class MaClasse:
     def __init__(self):
             MaClasse.essai = "ok"

objet = MaClasse()
objet2 = MaClasse()
print objet.essai

> ok

 
Sélectionnez
print objet2.essai

> ok

 
Sélectionnez
print MaClasse.essai

> ok

 
Sélectionnez
MaClasse.essai = "wow"
print objet.essai

> wow

 
Sélectionnez
print objet2.essai

> wow

Ici, les deux instances partagent donc bien le même attribut et changer la valeur de cet attribut en la préfixant du nom de la classe, même au moment de la réaffectation, permet bien de spécifier que ce n'est pas au niveau de l'instance que les choses se jouent et que par conséquent, toutes les instances hériteront de cette nouvelle valeur.
Si les choses ne sont pas clairement définies, il est possible de se retrouver avec de petites surprises. Continuons dans la foulée des lignes précédentes :

 
Sélectionnez
class MaClasse :
     essai = "ok"
     def affiche(self):
             print self.essai

objet = MaClasse()
objet.affiche()

> ok

 
Sélectionnez
objet2 = MaClasse()
objet2.affiche()

> ok

 
Sélectionnez
objet2.essai = "wow"
objet2.affiche()

> wow

 
Sélectionnez
objet.affiche()

> ok

 
Sélectionnez
print objet2.essai

> wow

 
Sélectionnez
MaClasse.essai = "test"
objet.affiche()

> test

 
Sélectionnez
objet2.affiche()

> wow

À interpréter cet exemple, on voit que l'attribut de classe qui a été redéfini dans le cadre d'une instance, ne prend plus en compte l'affectation de valeur faite à cet attribut de classe.
Ainsi il y a deux essais différents : celui de la classe créée en début de classe ou explicitement et l'attribut de la méthode. La méthode affiche fait référence à son propre essai qui hérite de celui de la classe au moment de la construction de l'instance, mais qui peut être redéfini séparément. Pour faire explicitement référence à l'attribut de classe, nous aurions dû écrire print MaClasse.essai et non pas self.essai.

V-J. Privatiser attributs et méthodes

Le but de la programmation objet est de mieux structurer le code pour mieux contrôler les contextes d'exécution. En effet, tout programme commence avec quelques petites lignes et finit souvent par plusieurs milliers et il devient alors plus difficile de retrouver ces marques et de bien continuer.
Les défauts de conception n'apparaissent en général pas au début des projets mais bien à la suite, lors des modifications successives, éventuellement réalisées par plusieurs personnes qui ont chacune leur vision des choses.
Différencier les attributs de classe et d'instance est une première approche améliorée, mais il est aussi possible de définir explicitement ce qui peut être de l'extérieur de l'objet. Jusqu'à présent, tout attribut et toute méthode peuvent être appelés du programme principal. Alors que dans la pratique certains d'entre eux seront certainement créés pour un usage interne en prétraitement d'informations. Ces attributs n'ont pas besoin, et ne devraient donc pas laisser cette tentation d'être accessible de l'extérieur.
Un petit exemple pour voir de quoi il s'agit :

 
Sélectionnez
class MaClasse:
    def __init__(self):
            self.__essai="Bonjour "
            self.essai="le monde"
    def affiche(self):
            print self.__essai
    def montre(self):
            print self.__essai

objet=MaClasse()
print objet.essai

Le résultat sera bien : le monde.

Maintenant essayons avec :

 
Sélectionnez
print objet.__essai
 
Sélectionnez
File "private_public.py", line 15, in <module>
    print objet.__essai
    AttributeError: MaClasse instance has no attribute '__essai'

L'interpréteur nous informe que la classe n'a pas d'attribut de ce nom. Ce n'est évidemment pas exactement vrai, mais il garde le secret sur la possibilité de manière à ne pas nous tenter : __essai est privé et ne peut être utilisé que depuis une méthode de la classe :

 
Sélectionnez
objet.affiche()

> Bonjour

Comment avons-nous fait pour rendre cette variable privée ? Nous avons simplement rajouté deux _ en début de nom. Remarquez au passage que essai et __essai sont considérés comme étant deux attributs différents par l'interpréteur alors que pour le lecteur ils seront évidemment très similaires.
La même opération peut être réalisée avec les méthodes :

 
Sélectionnez
class MaClasse:
    def __init__(self):
           self.__essai="Bonjour "
    def affiche(self):
           print self.essai
    def __montre(self):
           print self.__essai, self.essai

objet=MaClasse()
objet.__montre()

File "private_public.py", line 15, in <module>

 
Sélectionnez
objet.__montre()

AttributeError: MaClasse instance has no attribute '__montre'

alors que :

 
Sélectionnez
class MaClasse:
   def __init__(self):
          self.__essai="Bonjour "
          self.essai="le monde"
   def affiche(self):
          self.__montre()
   def __montre(self):
          print self.__essai, self.essai

objet=MaClasse()
objet.affiche()

renverra :

Bonjour le monde

En fait, il existe plusieurs notations. On trouvera souvent référence à _variable comme étant une convention d'écriture. Cette convention était utilisée à titre mnémotechnique dans les moments où Python ne gérait pas la privatisation des données. Une autre solution existe aussi : __variable__. Dans ce cas, la variable est absolument privée et rappellera les méthodes par défaut comme __init__, __del__… En fait, __variable ne produit une variable que partiellement variable qui est bien interdite d'accès hors du contexte mais qui peut toujours être appelée par le biais du chemin complet : instance._classe__variable.

V-K. Paramètres d'initialisation d'instances

Il ne sera pas rare, puisque c'est partiellement l'objet de la structuration en objet, d'avoir besoin de créer plusieurs instances de la même classe.
Cela est réalisable très simplement, en passant des paramètres à notre constructeur :

 
Sélectionnez
class MaClasse:
    def __init__(self, nom):
            self.__essai="Bonjour"
            self.essai="le monde,"
            self.nom=nom
    def affiche(self):
            print self.__essai, self.essai, self.nom

objet=MaClasse("Cedric")
objet.affiche()

Nous avons déclaré le paramètre nom puis l'avons associé à self.nom pour le réutiliser plus librement. 
Le résultat sera donc :

Bonjour le monde, Cedric

On passera en paramètre de constructeurs les éléments réellement indispensables à l'utilisation et à l'identification de cet objet dans le programme. Pour les paramètres moins fondamentaux, ils pourront être passés à l'appel d'une méthode. Il faudra bien sûr que celle-ci l'accepte au préalable :

 
Sélectionnez
class MaClasse:
    def __init__(self, nom):
            self.__essai="Bonjour"
            self.essai="le monde,"
            self.nom=nom
    def affiche(self, sexe):
            print self.__essai, self.essai, sexe, self.nom

objet=MaClasse("Cedric")
objet.affiche("Monsieur")

> Bonjour le monde, Monsieur Cedric

V-L. Étendre ou dériver une classe

Un autre cas de réutilisation qui ne sera pas rare, sera souvent d'avoir besoin de créer une variante de l'objet, avec plus de méthodes, des attributs différents ou avec d'autres différences. Dans ce cas, vous avez deux solutions :

  • soit utiliser la classe de base telle quelle et ne rien changer (cas lorsqu'on utilise des modules) ;
  • soit modifier la classe d'origine pour définir les nouveaux besoins et les y intégrer ;
  • soit créer une nouvelle classe qui sera basée sur la première mais qui prendra en compte spécifiquement ces différences.

Vous comprendrez à nous lire que cette dernière méthode est la plus fiable : elle permet de préserver une classe originale saine et exempte des cas particuliers qui sont alors simplement définis dans les classes filles.
Ce concept d'héritage est mis en œuvre simplement en passant le nom de la classe d'origine en paramètre de la classe fille :

 
Sélectionnez
class MaClasse:    def affiche(self):
              print self.nom
                                              
                                       
  objet=MaClasseFille("Cedric")
  objet.affiche()

    def __init__(self, nom):
            self.essai="Bonjour"
            self.nom=nom
    def affiche(self):
            print self.essai, self.nom

class MaClasseFille(MaClasse):
    def affiche(self):
            print self.nom                                    

objet=MaClasseFille("Cedric")
objet.affiche()

ce qui affiche

Bonjour Cedric

La classe file peut donc posséder ses propres méthodes, voire redéfinir les méthodes de sa classe mère :

 
Sélectionnez
class MaClasse:
    def __init__(self, nom):
            self.essai="Bonjour"
            self.nom=nom
    def affiche(self):
            print self.essai, self.nom

class MaClasseFille(MaClasse):
    def affiche(self):
            print self.nom                                 

objet=MaClasseFille("Cedric")
objet.affiche()

Avec cet exemple, la classe fille n'affichera que le nom passé en paramètre lors de l'instanciation alors que dans la classe mère, le nom était concaténé à « Bonjour ». Il sera ainsi possible de dériver énormément de chose sans toucher à l'original et ainsi diminuer le risque d'introduction de bogues dans d'autres applications qui utiliseraient aussi cette classe. Le code devient alors réellement plus simple à maintenir et chaque programme devient plus sûr. Les efforts peuvent être mutualisés sur plusieurs classes, voire plusieurs applications, car toute modification effectuée sur la classe mère sera automatiquement reportée sur les classes filles qui en hérite, ce qui est fondamental lors d'un débogage.

V-M. Les modules : partager des fonctions et objets entre programmes

Un module est une classe ou ensemble de classes qui sera spécifiquement utilisé comme ressources dans d'autres programmes. Il a en général été conçu comme tel et s'avère très pratique lors de tâches répétitives.
La règle veut que les modules portent le même nom que la classe qu'ils contiennent. Ainsi MaClasse.py serait le nom de module courant pour le fichier contenant la classe MaClasse. Bien sûr, le module pouvant en contenir plusieurs, cette règle est adaptable.
Au niveau du fichier contenant les classes il est fréquent d'ajouter une condition de test d'exécution (en général en fin de fichier) :

 
Sélectionnez
if __name__ == "__main__"
    pass
    # autres instructions au lieu de pass

Le but de cette condition est de définir ce qui doit être exécuté si le module est exécuté comme corps principal du programme, sinon, cette partie est simplement ignorée.

 
Sélectionnez
#!/usr/bin/env python
# -*- coding: utf-8 -*-

class MaClasse:
    def __init__(self, nom):
            self.essai="Bonjour"
            self.nom=nom
    def affiche(self):
            print self.essai, self.nom

class MaClasseFille(MaClasse):
    def affiche(self):
            print self.nom
                                                  
if __name__ == "__main__":
    pass

Enregistrez ce fichier avec l'extension .py.

V-M-1. Utilisation du module

Une fois votre module créé, vous pourrez alors l'importer pour le réutiliser dans tous vos projets  :

 
Sélectionnez
import MaClasse
objet = MaClasse.MaClasseFille("cedric")
objet.affiche()

Commentons un peu ce qui se passe ici :

  • on commence par importer le module on utilisant son nom, sans l'extension. Par défaut, Python cherche dans le PYTHONPATH et dans le répertoire courant ;
  • on instancie les objets en utilisant le nom du module puis le nom de la classe (ce qui n'était pas nécessaire jusqu'ici) ;
  • enfin, l'instance  peut alors jouer son rôle normalement.

La méthode import va charger l'ensemble du fichier en vue de son exploitation ultérieure.

 
Sélectionnez
from param_classe import MaClasseFille
objet = MaClasseFille("Cedric")
objet.affiche()

> Cedric

Si le gain ici n'est pas très important à cause de la simplicité de notre fichier module, remarquez qu'avec cette façon de faire, il n'est pas nécessaire de nommer le module lors de l'instanciation. Si cela a l'avantage de faire saisir un peu moins de texte, si vous devez charger plusieurs modules, il faudra être certain qu'il n'y aura pas de conflit dans le nom des classes et méthodes utilisées, problème qui est résolu avec l'utilisation d'import seul et la préservation de son espace de nom.


précédentsommairesuivant

Licence Creative Commons
Le contenu de cet article est rédigé par Floss Manuals et est mis à disposition selon les termes de la Licence Creative Commons Attribution 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2013 Developpez.com.