• PYTHON > les classes

      class NomDeLaClasse:

       

      constructeur

       

      class Personne:
          """Du blabla pour l'aide sur la classe"""
          def __init__(self):
              """constructeur avec les attributs"""
              self.nom = "Dupont"

       

      les noms de méthodes du genre __nomMethode__ sont des méthodes spéciales

       

      bernard = Personne()
      bernard
      <__main__.Personne object at 0x00B42570>
      bernard.nom
      'Dupont'

       

      Quand on tape Personne(), on appelle le constructeur de la classe Personne.

       

      On déconseille le objet.attribut = valeur pour changer la valeur d’un attribut

      accesseurs et mutateurs

      modifier un attribut

      objet.attribut = nouvelle_valeur

       

      class Personne:
          def __init__(self, nom, prenom):
              """Constructeur de la classe"""
              self.nom = nom
              self.prenom = prenom
              self.age = 33
              self.lieu_residence = "Paris"

      Et en images :

      bernard = Personne("Micado", "Bernard")

       

      Le premier paramètre doit être self

      Attributs de classe

      class Compteur:
          """Cette classe possède un attribut de classe qui s'incrémente à chaque fois que l'on crée un objet de ce type"""
          objets_crees = 0 # Le compteur vaut 0 au départ
          def __init__(self):
              """À chaque fois qu'on crée un objet, on incrémente le compteur"""
              Compteur.objets_crees += 1

       

      On définit notre attribut de classe directement dans le corps de la classe, sous la définition et ladocstring, avant la définition du constructeur. Quand on veut l’appeler dans le constructeur, on préfixe le nom de l’attribut de classe par le nom de la classe. Et on y accède de cette façon également, en dehors de la classe. Voyez plutôt :

       

      Compteur.objets_crees
      0
      a = Compteur() # On crée un premier objet
      Compteur.objets_crees
      1
      b = Compteur()
      Compteur.objets_crees
      2

       

      À chaque fois qu’on crée un objet de type Compteur, l’attribut de classe objets_crees s’incrémente de 1. Cela peut être utile d’avoir des attributs de classe, quand tous nos objets doivent avoir certaines données identiques. Nous aurons l’occasion d’en reparler par la suite.

      Les méthodes, la recette

      Les attributs sont des variables propres à notre objet, qui servent à le caractériser. Les méthodes sont plutôt des actions, comme nous l’avons vu dans la partie précédente, agissant sur l’objet. Par exemple, la méthode append de la classe list permet d’ajouter un élément dans l’objet list manipulé.

      Pour créer nos premières méthodes, nous allons modéliser… un tableau. Un tableau noir, oui c’est très bien.

      Notre tableau va posséder une surface (un attribut) sur laquelle on pourra écrire, que l’on pourra lire et effacer. Pour créer notre classe TableauNoir et notre attribut surface, vous ne devriez pas avoir de problème :

       

      class TableauNoir:
        ""surface sur laquelle on peut écrire, lire et effacer, par jeu de méthodes. L'attribut modifié est 'surface'"""
        def __init__(self):
            """Par défaut, notre surface est vide"""
            self.surface = ""

       

      Nous avons déjà créé une méthode, aussi vous ne devriez pas être trop surpris par la syntaxe que nous allons voir. Notre constructeur est en effet une méthode, elle en garde la syntaxe. Nous allons donc écrire notre méthodeecrirepour commencer.

       

      class TableauNoir:
          """surface sur laquelle on peut écrire, lire et effacer, par jeu de méthodes. L'attribut modifié est 'surface'"""
          def __init__(self):
              """Par défaut, notre surface est vide"""
              self.surface = ""
          def ecrire(self, message_a_ecrire):
              """Méthode pour écrire sur la surface. Si surface n'est pas vide, on saute une ligne avant de rajouter le message à écrire"""
              if self.surface != "":
              self.surface += "\n"
              self.surface += message_a_ecrire

      Passons aux tests :

      >>> tab = TableauNoir()
      >>> tab.surface
      ''
      >>> tab.ecrire("Coooool ! Ce sont les vacances !")
      >>> tab.surface
      "Coooool ! Ce sont les vacances !"
      >>> tab.ecrire("Joyeux Noël !")
      >>> tab.surface
      "Coooool ! Ce sont les vacances !\nJoyeux Noël !"
      >>> print(tab.surface)
      Coooool ! Ce sont les vacances !
      Joyeux Noël !

       

      La méthode ecrire se charge d’écrire sur notre surface, en rajoutant un saut de ligne pour séparer chaque message.

      On retrouve ici notre paramètreself. Il est temps de voir un peu plus en détail à quoi il sert.

      Le paramètre self

      Dans nos méthodes d’instance, qu’on appelle également des méthodes d’objet, on trouve dans la définition ce paramètreself. L’heure est venue de comprendre ce qu’il signifie.

      Une chose qui a son importance : quand vous créez un nouvel objet, ici un tableau noir, les attributs de l’objet sont propres à l’objet créé. C’est logique : si vous créez plusieurs tableaux noirs, ils ne vont pas tous avoir la même surface. Donc les attributs sont contenus dans l’objet.

      En revanche, les méthodes sont contenues dans la classe qui définit notre objet. C’est très important. Quand vous tapeztab.ecrire(…), Python va chercher la méthodeecrirenon pas dans l’objettab, mais dans la classeTableauNoir.

       

      >>> tab.ecrire
      <bound method TableauNoir.ecrire of <__main__.TableauNoir object at 0x00B3F3F0>>
      >>> TableauNoir.ecrire
      <function ecrire at 0x00BA5810>
      >>> help(TableauNoir.ecrire)
      Help on function ecrire in module __main__:
      ecrire(self, message_a_ecrire)
      Méthode permettant d'écrire sur la surface du tableau.
      Si la surface n'est pas vide, on saute une ligne avant de rajouter
      le message à écrire.
      >>> TableauNoir.ecrire(tab, "essai")
      >>> tab.surface
      'essai'

       

      Comme vous le voyez, quand vous tapeztab.ecrire(…), cela revient au même que si vous écrivezTableauNoir.ecrire(tab, …). Votre paramètreself, c’est l’objet qui appelle la méthode. C’est pour cette raison que vous modifiez la surface de l’objet en appelantself.surface.

      Pour résumer, quand vous devez travailler dans une méthode de l’objet sur l’objet lui-même, vous allez passer parself.

      Le nomselfest une très forte convention de nommage. Je vous déconseille de changer ce nom. Certains programmeurs, qui trouvent qu’écrireselfà chaque fois est excessivement long, l’abrègent en une unique lettres. Évitez ce raccourci. De manière générale, évitez de changer le nom. Une méthode d’instance travaille avec le paramètreself.

      N’est-ce pas effectivement plutôt long de devoir toujours travailler avecselfà chaque fois qu’on souhaite faire appel à l’objet ?

      Cela peut le sembler, oui. C’est d’ailleurs l’un des reproches qu’on fait au langage Python. Certains langages travaillent implicitement sur les attributs et méthodes d’un objet sans avoir besoin de les appeler spécifiquement. Mais c’est moins clair et cela peut susciter la confusion. En Python, dès qu’on voitself, on sait que c’est un attribut ou une méthode interne à l’objet qui va être appelé.

      Bon, voyons nos autres méthodes. Nous devons encore coderlirequi va se charger d’afficher notre surface eteffacerqui va effacer le contenu de notre surface. Si vous avez compris ce que je viens d’expliquer, vous devriez écrire ces méthodes sans aucun problème, elles sont très simples. Sinon, n’hésitez pas à relire, jusqu’à ce que le déclic se fasse.

       

      class TableauNoir:
        """surface sur laquelle on peut écrire, lire et effacer, par jeu de méthodes. L'attribut modifié est 'surface'"""
        def __init__(self):
            """Par défaut, notre surface est vide"""
            self.surface = ""
        def ecrire(self, message_a_ecrire):
            """Méthode pour écrire sur la surface. Si surface n'est pas vide, on saute une ligne avant de rajouter le message à écrire"""
            if self.surface != "":
                self.surface += "\n"
                self.surface += message_a_ecrire
        def lire(self):
            """méthode se charge d'afficher, grâce à print, la surface du tableau"""
            print(self.surface)
        def effacer(self):
            """méthode permet d'effacer la surface du tableau"""
            self.surface = ""

      Et encore une fois, le code de test :

      >>> tab = TableauNoir()
      >>> tab.lire()
      >>> tab.ecrire("Salut tout le monde.")
      >>> tab.ecrire("La forme ?")
      >>> tab.lire()
      Salut tout le monde.
      La forme ?
      >>> tab.effacer()
      >>> tab.lire()

       

      Et voilà ! Avec nos méthodes bien documentées, un petit coup dehelp(TableauNoir)et vous obtenez une belle description de l’utilité de votre classe. C’est très pratique, n’oubliez pas les docstrings.

      Méthodes de classe et méthodes statiques

      Comme on trouve des attributs propres à la classe, on trouve aussi des méthodes de classe, qui ne travaillent pas sur l’instanceselfmais sur la classe même. C’est un peu plus rare mais cela peut être utile parfois. Notre méthode de classe se définit exactement comme une méthode d’instance, à la différence qu’elle ne prend pas en premier paramètreself(l’instance de l’objet) maiscls(la classe de l’objet).

      En outre, on utilise ensuite une fonction built-in de Python pour lui faire comprendre qu’il s’agit d’une méthode de classe, pas d’une méthode d’instance.

       

      class Compteur:
      """Cette classe possède un attribut de classe qui s'incrémente à chaque fois que l'on crée un objet de ce type"""

      objets_crees = 0 # Le compteur vaut 0 au départ

      def __init__(self):

      """À chaque fois qu’on crée un objet, on incrémente le compteur"""

      Compteur.objets_crees += 1

      def combien(cls):

      """Méthode de classe affichant combien d’objets ont été créés"""

      print("Jusqu’à présent, {} objets ont été créés.".format(

      cls.objets_crees))

      combien = classmethod(combien)

      Voyons d’abord le résultat :

      >>> Compteur.combien()

      Jusqu’à présent, 0 objets ont été créés.

      >>> a = Compteur()

      >>> Compteur.combien()

      Jusqu’à présent, 1 objets ont été créés.

      >>> b = Compteur()

      >>> Compteur.combien()

      Jusqu’à présent, 2 objets ont été créés.

      >>

       

      Une méthode de classe prend en premier paramètre non passelfmaiscls. Ce paramètre contient la classe (iciCompteur).

      Notez que vous pouvez appeler la méthode de classe depuis un objet instancié sur la classe. Vous auriez par exemple pu écrirea.combien().

      Enfin, pour que Python reconnaisse une méthode de classe, il faut appeler la fonctionclassmethodqui prend en paramètre la méthode que l’on veut convertir et renvoie la méthode convertie.

      Si vous êtes un peu perdus, retenez la syntaxe de l’exemple. La plupart du temps, vous définirez des méthodes d’instance comme nous l’avons vu plutôt que des méthodes de classe.

      On peut également définir des méthodes statiques. Elles sont assez proches des méthodes de classe sauf qu’elles ne prennent aucun premier paramètre, niselfnicls. Elles travaillent donc indépendamment de toute donnée, aussi bien contenue dans l’instance de l’objet que dans la classe.

      Voici la syntaxe permettant de créer une méthode statique. Je ne veux pas vous surcharger d’informations et je vous laisse faire vos propres tests si cela vous intéresse :

       

      class Test:
        """Une classe de test tout simplement"""
        def afficher():
          """Fonction chargée d'afficher quelque chose"""
          print("On affiche la même chose.")
          print("peu importe les données de l'objet ou de la classe.")
          afficher = staticmethod(afficher)

       

      Si vous vous emmêlez un peu avec les attributs et méthodes de classe, ce n’est pas bien grave. Retenez surtout les attributs et méthodes d’instance, c’est essentiellement sur ceux-ci que je me suis attardé et c’est ceux que vous retrouverez la plupart du temps.

      Rappel : les noms de méthodes encadrés par deux soulignés de part et d’autre sont des méthodes spéciales. Ne nommez pas vos méthodes ainsi. Nous découvrirons plus tard ces méthodes particulières. Exemple de nom de méthode à éviter :__mamethode__.

      Un peu d’introspection

      Encore de la philosophie ?

      Eh bien… le terme d’introspection, je le reconnais, fait penser à quelque chose de plutôt abstrait. Pourtant, vous allez très vite comprendre l’idée qui se cache derrière : Python propose plusieurs techniques pour explorer un objet, connaître ses méthodes ou attributs.

      Quel est l’intérêt ? Quand on développe une classe, on sait généralement ce qu’il y a dedans, non ?

      En effet. L’utilité, à notre niveau, ne saute pas encore aux yeux. Et c’est pour cela que je ne vais pas trop m’attarder dessus. Si vous ne voyez pas l’intérêt, contentez-vous de garder dans un coin de votre tête les deux techniques que nous allons voir. Arrivera un jour où vous en aurez besoin ! Pour l’heure donc, voyons plutôt l’effet :

      La fonctiondir

      La première technique d’introspection que nous allons voir est la fonctiondir. Elle prend en paramètre un objet et renvoie la liste de ses attributs et méthodes.

       

      class Test:
          """Une classe de test tout simplement"""
          def __init__(self):
              """On définit dans le constructeur un unique attribut"""
              self.mon_attribut = "ok"
          def afficher_attribut(self):
              print("Mon attribut est {0}.".format(self.mon_attribut))
      
      # Créons un objet de la classe Test
      un_test = Test()
      un_test.afficher_attribut()
      Mon attribut est ok.
      dir(un_test)
      ['__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__',
       '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', 
      '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'afficher_attribut', 'mon_attribut']

       

      La fonctiondirrenvoie une liste comprenant le nom des attributs et méthodes de l’objet qu’on lui passe en paramètre. Vous pouvez remarquer que tout est mélangé, c’est normal : pour Python, les méthodes, les fonctions, les classes, les modules sont des objets. Ce qui différencie en premier lieu une variable d’une fonction, c’est qu’une fonction est exécutable (callable). La fonctiondirse contente de renvoyer tout ce qu’il y a dans l’objet, sans distinction.

      Euh, c’est quoi tout cela ? On n’a jamais défini toutes ces méthodes ou attributs !

      Non, en effet. Nous verrons plus loin qu’il s’agit de méthodes spéciales utiles à Python.

      L’attribut spécial__dict__

      Par défaut, quand vous développez une classe, tous les objets construits depuis cette classe posséderont un attribut spécial__dict__. Cet attribut est un dictionnaire qui contient en guise de clés les noms des attributs et, en tant que valeurs, les valeurs des attributs.

      Voyez plutôt :

       

      un_test = Test()
      un_test.__dict__
      {'mon_attribut': 'ok'}

       

      Pourquoi « attribut spécial » ?

      C’est un attribut un peu particulier car ce n’est pas vous qui le créez, c’est Python. Il est entouré de deux signes soulignés__de part et d’autre, ce qui traduit qu’il a une signification pour Python et n’est pas un attribut « standard ». Vous verrez plus loin dans ce cours des méthodes spéciales qui reprennent la même syntaxe.

      Peut-on modifier ce dictionnaire ?

      Vous le pouvez. Sachez qu’en modifiant la valeur de l’attribut, vous modifiez aussi l’attribut dans l’objet.

       

      >>> un_test.__dict__["mon_attribut"] = "plus ok"

      >>> un_test.afficher_attribut()

      Mon attribut est plus ok.

      >>>

       

      De manière générale, ne faites appel à l’introspection que si vous avez une bonne raison de le faire et évitez ce genre de syntaxe. Il est quand même plus propre d’écrireobjet.attribut = valeurqueobjet.__dict__[nom_attribut] = valeur.

      Nous n’irons pas plus loin dans ce chapitre. Je pense que vous découvrirez dans la suite de ce livre l’utilité des deux méthodes que je vous ai montrées.

      En résumé

      On définit une classe en suivant la syntaxeclass NomClasse:.

      Les méthodes se définissent comme des fonctions, sauf qu’elles se trouvent dans le corps de la classe.

      Les méthodes d’instance prennent en premier paramètreself, l’instance de l’objet manipulé.

      On construit une instance de classe en appelant son constructeur, une méthode d’instance appelée__init__.

      On définit les attributs d’une instance dans le constructeur de sa classe, en suivant cette syntaxe :self.nom_attribut = valeur.

 

Aucun commentaire

 

Laissez un commentaire