Aller au contenu

Vocabulaire de la POO

I. Introduction⚓

Le paradigme objet

Il existe différentes maniÚres de voir la programmation, on parle de différents paradigmes de programmation. L'un d'eux est le paradigme objet. Lorsque l'on programme en utilisant ce paradigme on parle de programmation objet ou de programmation orientée objet (abrégé POO, ou OOP en anglais pour « Object-oriented programming »). Nous nous limiterons cette année à une introduction de la programmation objet.

Crédits

L'introduction qui suit est en trĂšs grande parti extraite du livre "Informatique" de la collection Fluoresciences de l'Ă©diteur Dunod

Auteurs : Delacroix, Joëlle; Barthélémy, François; Fournier-S'niehotta, Raphaël; Gil-Michalon, Isabelle; Lambert, Amélie; Plateau, AgnÚs; Rovedakis, Stéphane; Simonot, Marianne; Thion, Virginie; Waymel, Emmanuel. Informatique (Fluoresciences) (French Edition)

Les objets

La programmation orientĂ©e objet permet de structurer les logiciels en un assemblage d’entitĂ©s indĂ©pendantes qui interagissent. Ces entitĂ©s sont appelĂ©es des objets.

Prenons l’exemple d’un jeu de type tower defense. Il s’agit d’un jeu vidĂ©o dont l’objectif est d’empĂȘcher des vagues d’ennemis d’atteindre une certaine zone en plaçant sur le terrain des tours dĂ©fensives. Les tours tirent sur les ennemis Ă  leur portĂ©e et se diffĂ©rencient les unes des autres selon des caractĂ©ristiques comme la portĂ©e de leur attaque, les dĂ©gĂąts causĂ©s etc. Chaque ennemi tuĂ© rapporte des points qui permettront d’acheter de nouvelles tours ou d’amĂ©liorer les tours existantes.

Nous aurons un objet terrain, des objets ennemis et des objets tours. Les trois points Ă  retenir sur les objets sont les suivants :
1. Chaque objet possÚde ses propres caractéristiques : le coût, les dégùts causés, la portée pour les tours, la résistance, la rapidité, les points de vie pour les ennemis etc.
2. Les objets savent faire des choses. C’est ce que l’on appelle leur comportement : les ennemis savent se dĂ©placer, les tours savent attaquer.
3. Les objets ont des interactions, peuvent communiquer : les ennemis interagissent avec le terrain pour savoir vers oĂč ils peuvent aller, les tours interagissent avec les ennemis en les visant ou bien en leur infligeant des dĂ©gĂąts.

L’élaboration d’un programme objet revient donc Ă  trouver les objets permettant d’organiser efficacement le programme et Ă  dĂ©finir leurs interactions.

Cette façon de structurer les programmes en entités indépendantes qui interagissent offre de nombreux avantages :

  • L’élaboration du logiciel est facilement partageable entre plusieurs programmeurs.
  • Un objet crĂ©Ă© pour un logiciel pourra facilement ĂȘtre rĂ©utilisĂ© dans un autre programme.
  • Le logiciel sera plus facile Ă  maintenir et Ă  faire Ă©voluer. On pourra adapter le logiciel Ă  de nouvelles exigences en modifiant uniquement le code des objets concernĂ©s par ce changement ou bien en ajoutant de nouveaux objets. Le reste du code n’aura pas besoin d’ĂȘtre modifiĂ©.

Les classes

Un programme est constituĂ© d’objets qui interagissent, mais certains objets relĂšvent d’une mĂȘme catĂ©gorie. Notre jeu vidĂ©o est constituĂ© de centaines d’objets qui se rĂ©partissent dans trois catĂ©gories possibles : les tours, les terrains et les ennemis.

Relever d’une mĂȘme catĂ©gorie signifie qu’il existe des caractĂ©ristiques et des comportements communs. Les caractĂ©ristiques des tours sont : les dĂ©gĂąts qu’elles infligent, la portĂ©e de leurs armes, leur coĂ»t et leur capacitĂ© Ă  attaquer. En programmation objet, les catĂ©gories s’appellent des classes.

L’une des tours lance des flĂšches et l’autre des boules de feu. En programmation objet, on dĂ©finit des classes et on crĂ©e des objets, exemplaires de ces classes. Faisons une analogie avec un moulage : une fois le moule (la classe) fabriquĂ©, il sert Ă  crĂ©er autant d’exemplaires similaires (les objets). Les objets sont des instances d’une classe.

Pour Ă©crire notre jeu en objet, nous aurions alors :

  • Ă  dĂ©finir trois classes : Tour, Ennemi, Terrain,
  • Ă  coder l’interaction avec l’utilisateur
  • puis Ă  crĂ©er des dizaines et des dizaines d’objets tours, d’objets ennemis et un terrain.

La dĂ©finition d’une classe permet d’introduire de nouveaux types adaptĂ©s Ă  notre application. Les variables pourront recevoir des valeurs de ces nouveaux types et, tout comme une variable qui contient un entier ne peut se voir appliquer que des opĂ©rations dĂ©finies sur les entiers, une variable contenant une valeur instance d’une classe ne pourra se voir appliquer que les comportements prĂ©vus dans la classe.

DĂ©finitions

En programmation orientĂ©e objet, un programme est un ensemble d’entitĂ©s qui interagissent. Ces entitĂ©s sont appelĂ©es des objets. Un objet possĂšde un Ă©tat (les donnĂ©es qui le caractĂ©rise) et des comportements (ce qu’il sait faire).

Une classe permet de dĂ©finir une famille d’objets. À partir d’une classe, on peut crĂ©er autant d’objets que l’on veut. Ce sont des exemplaires, on dit en fait des instances de la classe.

Le mot clĂ© class⚓

Le mot clé class

Pour dĂ©finir une classe en Python, on utilise le mot-clĂ© class. C’est une nouvelle instruction composĂ©e qui doit donc ĂȘtre suivie de « : ». À l’intĂ©rieur de la classe (c’est-Ă -dire dans tout le texte indentĂ© qui suit l’entĂȘte avec le mot class), on dĂ©finit les caractĂ©ristiques et les comportements communs aux objets de la classe. Les caractĂ©ristiques s’appellent les attributs et les comportements s’appellent les mĂ©thodes.

Exemple

class Ennemi :
    pass

À la place de pass viendront les attributs et mĂ©thodes de cette classe.
Une dĂ©finition de classe est une instruction comme une autre en Python. On peut donc la mettre n’importe oĂč. En pratique, on Ă©crit un module par classe et on importe le module. Par convention, le nom d’une classe commence par une majuscule.

CrĂ©er des objets Ă  partir d'une classe⚓

Des objets Ă  partir d'une classe

Une classe Ă©tant un moule, l’importation d’une classe ne produit rien de concret sinon de donner la possibilitĂ© de crĂ©er des objets instances de cette classe, des ennemis concrets. Ceci se fait de la façon suivante :

Exemple

grosMechant = Ennemi()
unAutreEnnemi = Ennemi()

grosMechant et unAutreEnnemi sont deux ennemis concrets fabriquĂ©s Ă  partir du mĂȘme moule.

Attributs et constructeurs⚓

Exemple

Pour l’instant, notre classe est une coquille vide. Il faut maintenant y ajouter les caractĂ©ristiques communes aux ennemis. Dans notre version simplifiĂ©e, les caractĂ©ristiques seront les suivantes :

  • leur position sur le terrain repĂ©rĂ©e par une position en X et une position en Y ;
  • leurs points de vie ;
  • leur rapiditĂ© ;

Vocabulaire

En terminologie objet, il faut dĂ©finir dans la classe Ennemi quatre attributs ou variables d’instances. Ceci se fait de la façon suivante :

Exemple

class Ennemi :
    def __init__(self) :
        self.posX = 0
        self.posY = 0
        self.pv = 100
        self.rapidite = 2

__init__

Nous avons dĂ©jĂ  rencontrĂ© le mot clĂ© def pour dĂ©finir des fonctions. Une classe est constituĂ©e essentiellement d’une suite de dĂ©finitions de fonctions qui reprĂ©sentent les fonctions que l’on va pouvoir utiliser sur les objets instances de la classe. Les fonctions internes aux classes s’appellent des mĂ©thodes.

La mĂ©thode __init__ est une mĂ©thode particuliĂšre qu’on appelle le constructeur de la classe. Cette mĂ©thode est utilisĂ©e lors de la crĂ©ation des objets. Son paramĂštre self reprĂ©sente l’objet qui va ĂȘtre crĂ©e.

Attention, il y a deux tirets underscore avant et aprĂšs le mot init .

Attributs

Le corps de __init__ indique qu’à chaque fois que l’on crĂ©e un objet ennemi, il faut lui associer quatre variables qui lui sont propres, nommĂ©es posX, posY, pv et rapidite et les initialiser avec les valeurs respectives 0, 0, 100 et 2. Tous les ennemis possĂšderont ces quatre variables. Ces variables s’appellent les attributs ou les variables d’instance de la classe. Ce sont bien des caractĂ©ristiques communes aux ennemis. Mais ils possĂšdent chacun leur propre copie de ces variables : leurs valeurs pourront donc Ă©voluer de façon indĂ©pendante.

AccĂ©der aux attributs des objets d'une classe⚓

Exemple

Nos deux objets grosMechant et unAutreEnnemi naissent avec les mĂȘmes caractĂ©ristiques et les mĂȘmes valeurs pour ces caractĂ©ristiques. Nous pouvons accĂ©der aux valeurs de ces attributs pour chacun d’entre eux au moyen de la notation pointĂ©e.

grosMechant = Ennemi()
unAutreEnnemi = Ennemi()
grosMechant.posX
Testons

Exécuter le script ci-dessous :

###(DĂ©s-)Active le code aprĂšs la ligne # Tests (insensible Ă  la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Ctrl+Clic pour inverser les colonnes)
Entrer ou sortir du mode "plein Ă©cran"
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activĂ©, le texte copiĂ© dans le terminal est joint sur une seule ligne avant d'ĂȘtre copiĂ© dans le presse-papier

Dans la console située en dessous du script, recopier et exécuter ligne par ligne à la main (sans copier/coller) :

>>> grosMechant.posX
>>> grosMechant.pv
>>> unAutreEnnemi.pv
>>> grosMechant.pv = 50
>>> grosMechant.pv
>>> unAutreEnnemi.pv

Résumé

grosMechant.pv dĂ©signe l’attribut pv de grosMechant, unAutreEnnemi.pv dĂ©signe l’attribut pv de unAutreEnnemi. On peut modifier la valeur des points de vie de grosMechant cela n’affecte en rien les points de vie de unAutreEnnemi. Les objets d’une mĂȘme classe ont donc les mĂȘmes attributs. Les valeurs de ces attributs sont propres Ă  chaque objet et Ă©voluent de façon indĂ©pendante au fil de l’exĂ©cution du programme : on peut tout Ă  fait imaginer que les points de vie de grosMechant tombent Ă  0 au bout de trois tours alors qu’unAutreEnnemi survit jusqu’au bout.

Des constructeurs plus souples⚓

Des paramĂštres

  • Avec le constructeur que nous avons Ă©crit, tous les objets de la classe Ennemi sont initialisĂ©s Ă  la crĂ©ation avec les mĂȘmes valeurs. Ceci n’est pas trĂšs pratique. Nous aimerions pouvoir crĂ©er un vrai gros mĂ©chant trĂšs lent mais avec plein de points de vie et un ennemi moins costaud mais beaucoup plus rapide. En d’autres termes, nous aimerions pouvoir choisir les valeurs des attributs rapidite et pv au moment de la crĂ©ation (instanciation) d’un objet.

  • Comme __init__ est une mĂ©thode comme une autre, il suffit de lui ajouter des paramĂštres permettant de transmettre les valeurs que l’on souhaite pour les attributs dont l’initialisation doit varier. Dans notre cas, nous allons ajouter deux paramĂštres r et p pour fixer la valeur de ces deux attributs. Il suffira alors d’initialiser self.rapidite avec l’une et self.pv avec l’autre. Nous n’ajoutons pas de paramĂštres permettant de transmettre une valeur pour posX et posY car nous voulons que tous les ennemis partent du mĂȘme endroit.

Testons

Nous avons maintenant le droit de transmettre deux valeurs lorsque nous créons des objets Ennemi.

Exécuter le script ci-dessous :

###(DĂ©s-)Active le code aprĂšs la ligne # Tests (insensible Ă  la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Ctrl+Clic pour inverser les colonnes)
Entrer ou sortir du mode "plein Ă©cran"
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activĂ©, le texte copiĂ© dans le terminal est joint sur une seule ligne avant d'ĂȘtre copiĂ© dans le presse-papier

Dans la console située en dessous du script, recopier et exécuter ligne par ligne à la main (sans copier/coller) :

>>> LePetitRapide.pv
>>> LePetitRapide.rapidite
>>> LeVraiGrosMechant.pv
>>> LeVraiGrosMechant.rapidite
>>> LeVraiGrosMechant.posX
>>> LePetitRapide.posX

👉 CrĂ©ez votre propre instance de la classe Ennemi_2, et faites afficher ses attributs comme ci-dessus.

Les mĂ©thodes⚓

Les méthodes

Nous avons dit en introduction que dĂ©finir une classe c’est dĂ©finir des attributs et les comportements communs aux objets de la classe. Nous savons maintenant dĂ©finir les attributs. Qu’en est-il des comportements ?

Les comportements reprĂ©sentent ce que les objets savent faire. Dans notre version, nous voulons doter les ennemis de capacitĂ©s trĂšs rudimentaires : se dĂ©placer vers un nouveau point, recevoir des points de dĂ©gĂąts et afficher leurs caractĂ©ristiques. Pour dĂ©finir un comportement, il suffit d’ajouter une mĂ©thode Ă  notre classe. Ces mĂ©thodes auront toujours self comme premier paramĂštre de façon Ă  dĂ©signer l’objet sur lequel va s’appliquer la mĂ©thode. Lors de l’appel de la mĂ©thode, la notation pointĂ©e nous permettra de dĂ©signer l’objet qui prendra la place de self.

La méthode recevoirDegats

Un ennemi doit pouvoir subir un certain nombre de dĂ©gĂąts. Nous ajoutons donc une mĂ©thode recoitDegats dans la classe Ennemi. Cette mĂ©thode possĂšde deux paramĂštres : self, qui dĂ©signe l’objet ennemi qui subit les dĂ©gĂąts et deg, le nombre de points de dĂ©gĂąts qu’il doit recevoir. Lorsqu’un ennemi reçoit des points de dĂ©gĂąts, cela doit modifier ses points de vie. Puisque self dĂ©signe l’objet sur lequel on applique la mĂ©thode, self.pv dĂ©signe son attribut personnel pv. Il suffit donc d’enlever la valeur de deg Ă  cet attribut.

Exécuter le script ci-dessous :

###(DĂ©s-)Active le code aprĂšs la ligne # Tests (insensible Ă  la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Ctrl+Clic pour inverser les colonnes)
Entrer ou sortir du mode "plein Ă©cran"
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activĂ©, le texte copiĂ© dans le terminal est joint sur une seule ligne avant d'ĂȘtre copiĂ© dans le presse-papier

👉 Jouez un peu avec la mĂ©thode recoitDegats

MĂ©thode

Une mĂ©thode se dĂ©finit donc exactement comme une fonction Ă  la seule diffĂ©rence que son premier paramĂštre est toujours self qui dĂ©signe l’objet sur lequel s’appliquera la mĂ©thode. Par voie de consĂ©quence, le corps de la mĂ©thode peut faire rĂ©fĂ©rence Ă  self.

Remarque

đŸŒ” La mĂ©thode recoitDegats recoit 2 arguments : self et deg.

Mais dans l'appel on Ă©crit : mechant.recoitDegats(25)

Donc un seul paramĂštre !!!

self est l'objet lui mĂȘme, ici nommĂ© mechant.

Les méthodes seDeplace et affiche

Un ennemi doit pouvoir se dĂ©placer vers une nouvelle position. Nous ajoutons donc une mĂ©thode seDeplace dans la classe Ennemi_4. Cette mĂ©thode possĂšde trois paramĂštres : self et deux entiers reprĂ©sentant les coordonnĂ©es de la nouvelle la nouvelle position. Cela doit modifier les attributs posX et posY. Pour afficher, on se contente d’afficher les valeurs de tous les attributs de l’objet. Seul self est en paramĂštre.

###(DĂ©s-)Active le code aprĂšs la ligne # Tests (insensible Ă  la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Ctrl+Clic pour inverser les colonnes)
Entrer ou sortir du mode "plein Ă©cran"
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activĂ©, le texte copiĂ© dans le terminal est joint sur une seule ligne avant d'ĂȘtre copiĂ© dans le presse-papier

  • Maintenant que notre classe est enrichie de trois comportements, nous pouvons les utiliser sur des objets instances de la classe Ennemi_4.

  • Pour faire se dĂ©placer grosMechant au point d’abscisse 5 et d’ordonnĂ©e 8, il suffit d’écrire grosMechant.seDeplace( 5,8). Le corps de la mĂ©thode va ĂȘtre exĂ©cutĂ© dans un contexte oĂč self dĂ©signera grosMechant, nouveauX dĂ©signera 5 et nouveauY dĂ©signera 8 et modifiera uniquement les attributs posX et posY de grosMechant.

  • Soulignons le fait que si self doit toujours ĂȘtre le premier paramĂštre d’une mĂ©thode, il n’est pas prĂ©sent entre les parenthĂšses lors de l’appel de la mĂ©thode. Si une mĂ©thode dans une classe C est de la forme nomMethode( self, par1,.., parn) alors on l’utilise de la façon suivante : unObjet.uneMethode( val1,... valn).
  • Le corps de la mĂ©thode sera alors exĂ©cutĂ© avec unObjet Ă  la place de self, val1 Ă  la place de par1, 
et valn Ă  la place de parn.

Dans la console située en dessous du script, recopier et exécuter ligne par ligne à la main (sans copier/coller) :

>>> grosMechant = Ennemi_4(100, 2)
>>> unAutreEnnemi = Ennemi_4(50, 4)
>>> grosMechant.affiche()
>>> unAutreEnnemi.affiche()
>>> grosMechant.seDeplace(5, 8)
>>> grosMechant.affiche()
>>> unAutreEnnemi.affiche()
>>> grosMechant.recoitDegats(50)
>>> unAutreEnnemi.recoitDegats(10)
>>> grosMechant.affiche()
>>> unAutreEnnemi.affiche()

MĂ©thode et classe

👉 Les mĂ©thodes dĂ©finies dans une classe ne sont applicables que sur les objets instances de la classe. Inversement, un objet instance d’une classe ne peut se voir appliquer que les mĂ©thodes de cette classe.

Tester

###(DĂ©s-)Active le code aprĂšs la ligne # Tests (insensible Ă  la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Ctrl+Clic pour inverser les colonnes)
Entrer ou sortir du mode "plein Ă©cran"
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activĂ©, le texte copiĂ© dans le terminal est joint sur une seule ligne avant d'ĂȘtre copiĂ© dans le presse-papier

###(DĂ©s-)Active le code aprĂšs la ligne # Tests (insensible Ă  la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Ctrl+Clic pour inverser les colonnes)
Entrer ou sortir du mode "plein Ă©cran"
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activĂ©, le texte copiĂ© dans le terminal est joint sur une seule ligne avant d'ĂȘtre copiĂ© dans le presse-papier

Solution

Dans ces exemples, a contient une valeur de type int mais pas de valeur de type Ennemi_4. Les mĂ©thodes de Ennemi_4 ne lui sont donc pas applicables. De la mĂȘme façon, grosMechant contient une valeur de type Ennemi_4 et + n’est pas dĂ©finie sur les valeurs de ce type.

Les mĂ©thodes peuvent renvoyer un rĂ©sultat⚓

Les méthodes peuvent renvoyer un résultat

Les mĂ©thodes ont le plus souvent pour rĂŽle de modifier l’état de l’objet sur lequel elles s’appliquent. Elles peuvent aussi, comme les fonctions, renvoyer un rĂ©sultat. Par exemple, dans notre classe, nous pouvons dĂ©finir une mĂ©thode estVivant qui teste si l’ennemi sur lequel elle s’applique a encore des points de vie.

Testons

Exécuter le script ci-dessous :

###(DĂ©s-)Active le code aprĂšs la ligne # Tests (insensible Ă  la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Ctrl+Clic pour inverser les colonnes)
Entrer ou sortir du mode "plein Ă©cran"
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activĂ©, le texte copiĂ© dans le terminal est joint sur une seule ligne avant d'ĂȘtre copiĂ© dans le presse-papier

Dans la console située en dessous du script, recopier et exécuter ligne par ligne à la main (sans copier/coller) :

>>> grosMechant.estVivant()

Des mĂ©thodes spĂ©ciales⚓

La méthode spéciale __str__()

Nous connaissons la mĂ©thode print() pour les valeurs de type simple. Cette mĂ©thode est aussi applicable sur des objets. Par dĂ©faut, elle affiche des informations inexploitables. Vous pouvez choisir ce qu’elle affiche pour les classes que vous dĂ©finissez en ajoutant dans ces classes la mĂ©thode __str__(). La mĂ©thode __str__() doit renvoyer une chaĂźne de caractĂšres. print() utilisera cette chaĂźne de caractĂšres pour faire l’affichage. Ceci est beaucoup plus souple que de dĂ©finir des mĂ©thodes afficher(). Pour la classe Ennemi, on remplacerait la mĂ©thode affiche() par la mĂ©thode __str__() dĂ©finie ainsi :

Testons

Exécuter le script ci-dessous :

###(DĂ©s-)Active le code aprĂšs la ligne # Tests (insensible Ă  la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Ctrl+Clic pour inverser les colonnes)
Entrer ou sortir du mode "plein Ă©cran"
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activĂ©, le texte copiĂ© dans le terminal est joint sur une seule ligne avant d'ĂȘtre copiĂ© dans le presse-papier

😊 Nous pouvons Ă  prĂ©sent afficher un ennemi en utilisant print().

👉 Faites vos propres essais ...

Objets et rĂ©fĂ©rences⚓

Objets et références

Lorsque nous lions une variable Ă  un objet, la variable ne contient pas l’objet lui- mĂȘme, mais une rĂ©fĂ©rence Ă  l’objet, c’est-Ă -dire l’adresse mĂ©moire oĂč se situe l’objet. DĂšs lors, deux variables peuvent rĂ©fĂ©rencer le mĂȘme objet. Ce sont alors deux moyens d’accĂšs au mĂȘme objet. On peut donc modifier l’objet par l’intermĂ©diaire de l’une des variables comme de l’autre. Cette possibilitĂ© ouvre la voie Ă  de nombreuses erreurs de programmation. Il faut donc ĂȘtre capable de faire la diffĂ©rence entre deux variables rĂ©fĂ©rençant des objets ayant la mĂȘme valeur et deux variables qui rĂ©fĂ©rencent le mĂȘme objet.

Testons

Exécuter le script ci-dessous :

###(DĂ©s-)Active le code aprĂšs la ligne # Tests (insensible Ă  la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Ctrl+Clic pour inverser les colonnes)
Entrer ou sortir du mode "plein Ă©cran"
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activĂ©, le texte copiĂ© dans le terminal est joint sur une seule ligne avant d'ĂȘtre copiĂ© dans le presse-papier

Dans la console située en dessous du script, recopier et exécuter ligne par ligne à la main (sans copier/coller) :

>>> x
>>> y
>>> z

Objets et références

La valeur de la variable x est donnĂ©e par Ennemi_6( 10,20). Ceci crĂ©e un nouvel objet ennemi en mĂ©moire et x reçoit comme valeur l’adresse de ce nouvel objet qui nous est indiquĂ©e.

Il en est de mĂȘme pour y : il y a crĂ©ation d’un nouvel objet stockĂ© dans une adresse mĂ©moire diffĂ©rente et y reçoit cette adresse comme valeur. En revanche,

z = y donne à z la valeur de y à savoir l’adresse de y

Les variables x et y, bien qu’étant liĂ©es Ă  des objets ayant les mĂȘmes valeurs, sont indĂ©pendantes : la modification de l’une n’a aucun impact sur l’autre. Ce n’est pas la mĂȘme chose pour y et z comme l’indique l’exemple suivant :

Testons

Exécuter le script ci-dessous :

###(DĂ©s-)Active le code aprĂšs la ligne # Tests (insensible Ă  la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Ctrl+Clic pour inverser les colonnes)
Entrer ou sortir du mode "plein Ă©cran"
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activĂ©, le texte copiĂ© dans le terminal est joint sur une seule ligne avant d'ĂȘtre copiĂ© dans le presse-papier

Objets et références

Le fait d’avoir modifiĂ© les points de vie de y a aussi modifiĂ© ceux de z.

đŸŒ” Cette notion est rendue confuse du fait de l’abus de langage que nous faisons : nous ne devrions pas dire que nous modifions les points de vie de y mais que nous modifions les points de vie de l’objet rĂ©fĂ©rencĂ© par y.

CrĂ©er des clones⚓

Objets et références

🙃 Le fait que deux variables puissent partager le mĂȘme objet doit ĂȘtre utilisĂ© Ă  bon escient. Prenons l’exemple d’un jeu contenant des monstres qui ont un pouvoir de clonage. La classe suivante reprĂ©sente ces monstres et contient deux versions possibles de la mĂ©thode permettant de crĂ©er un clone.

Testons

Exécuter le script ci-dessous :

###(DĂ©s-)Active le code aprĂšs la ligne # Tests (insensible Ă  la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Ctrl+Clic pour inverser les colonnes)
Entrer ou sortir du mode "plein Ă©cran"
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activĂ©, le texte copiĂ© dans le terminal est joint sur une seule ligne avant d'ĂȘtre copiĂ© dans le presse-papier

Résumé

Dans notre exemple, mic a reçu huit points de dĂ©gĂąts et a donc vingt-deux points de vie, alors que clone1 qui a Ă©tĂ© crĂ©Ă© par copie de mic au moment oĂč celui-ci avait vingt-cinq points de vie a reçu vingt-cinq points de dĂ©gĂąts et est donc mort.

La mĂ©thode seClonerV2() est tout Ă  fait diffĂ©rente. Au lieu de crĂ©er un nouveau monstre ayant les mĂȘmes valeurs que celles du monstre original, elle renvoie self, c’est-Ă -dire une rĂ©fĂ©rence au monstre original. Toute attaque sur le monstre affaiblira son clone et inversement.

Ici, clone2 se retrouve avec sept points de vie : il est affectĂ© par les dĂ©gĂąts qu’il reçoit (15) mais aussi par ceux que moc reçoit (3). Il en est de mĂȘme pour moc. Clairement, pour crĂ©er un clone, c’est la premiĂšre version qu’il faut choisir.

Remarque

😱 Nous laissons au lecteur le soin d’imaginer le temps que l’on peut mettre Ă  repĂ©rer l’erreur lorsque l’on a Ă©crit la seconde version sans en connaĂźtre les consĂ©quences...

🐘 Ce qu'il faut retenir⚓

À savoir 💚

  • Une classe permet de dĂ©finir un ensemble d’objets qui ont des caractĂ©ristiques communes. C’est un moule permettant de crĂ©er une infinitĂ© d’objets diffĂ©rents dans leurs valeurs mais similaires dans leur structure. Ces objets sont les instances de la classe

  • DĂ©finir une classe c’est dĂ©finir l’ensemble des attributs et des mĂ©thodes caractĂ©risant tous les objets instances de la classe.

  • Les attributs sont en gĂ©nĂ©ral dĂ©clarĂ©s privĂ©s afin d’en interdire l’accĂšs Ă  l’extĂ©rieur de la classe. C’est ce qu’on appelle l’encapsulation.

  • La mĂ©thode __init__ est le constructeur de la classe. Elle est utilisĂ©e Ă  la crĂ©ation des objets de la classe et initialise les valeurs des attributs de l’objet.

  • Pour utiliser une mĂ©thode m(self, 
) d’une classe C, il faut avoir un objet mon_objet instance de la classe C et faire : mon_objet.m(...)

II. Vocabulaire de la programmation objet⚓

Les objets

La programmation objet consiste Ă  regrouper donnĂ©es et traitements dans une mĂȘme structure appelĂ©e objet. Elle possĂšde l'avantage de localiser en un mĂȘme endroit toute l'implĂ©mentation d'une structure de donnĂ©es abstraite.

Prenons une voiture comme exemple

Une voiture peut ĂȘtre considĂ©rer comme un objet.

  • On peut lui associer des informations comme sa couleur, sa marque et sa catĂ©gorie : il s'agit des attributs de notre objet.

  • On peut Ă©galement dĂ©finir des mĂ©canismes concernant cet objet comme dĂ©marrer, accĂ©lĂ©rer, freiner, klaxonner : il s'agit des mĂ©thodes qui vont s'appliquer sur notre objet.

Les objets

ConcrÚtement, un objet est une structure de données abstraite regroupant :

  • des donnĂ©es associĂ©es Ă  cet l'objet que l'on appelle des attributs.
  • des fonctions (ou procĂ©dures) s'appliquant sur l'objet que l'on appelle des mĂ©thodes.

Exemple de classe

Reprenons l'exemple de la voiture

Ses attributs ( couleur, marque, ...) et mĂ©thodes ( dĂ©marrer, accĂ©lerer, ...) sont rĂ©unis dans ce qu'on appelle une classe qui est donc un modĂšle (moule) dĂ©crivant un objet, ici la voiture. On va donc dĂ©finir une classe Voiture qui va ĂȘtre le moule (modĂšle) pour la fabrication de tous les objets voitures. On peut la schĂ©matiser ainsi :

    classDiagram
    class Voiture{
        String Couleur
        String Marque
        String Catégorie
        DĂ©marrer()
        Accélérer()
        Freiner()
        Klaxonner()
    }

Les objets sont ensuite créés à partir de ce moule. On peut fabriquer deux objets clio et c3, qui sont deux instances de la classe Voiture en écrivant simplement les deux instructions suivantes :

🐍 Script Python
clio = Voiture()
c3 = Voiture()

Les instances

On va donc pouvoir créer facilement des objets Voiture grùce à cette classe (moule).

Il suffit pour les construire d'utiliser le nom de la classe (qui est aussi le nom du constructeur d'objets de cette classe). Chaque objet ainsi créé est une instance de la classe.

Remarque

  • Les mĂ©thodes dĂ©finies dans une classe ne sont applicables que sur les objets instances de la classe.
  • Inversement, un objet instance d’une classe ne peut se voir appliquer que les mĂ©thodes de cette classe.

AccÚs aux attributs et aux méthodes

Pour accéder aux attributs et aux méthodes d'une classe on utilise la notation pointée.

Dans notre exemple :

  • clio.marque donne la marque de l'objet clio, c'est Ă  dire Renault
  • clio.klaxonner() va faire klaxonner notre voiture (virtuelle). Nous reviendrons sur cela un peu plus loin.

III. Classes et objets en Python⚓

En Python, tout est objet !

Vous ne le saviez sans doute pas, mais les objets vous connaissez déjà (et oui !)

Les listes en Python

Les listes sont un type abstrait list dans python; et vous utilisez la notation pointée pour ajouter un élément.

Recopier une par une les lignes suivantes, et les exécuter une par une.

🐍 Console Python
>>> ma_liste = [2, 3, 5]
>>> type(ma_liste)
>>> ma_liste.append(8)
>>> ma_liste

Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activĂ©, le texte copiĂ© dans le terminal est joint sur une seule ligne avant d'ĂȘtre copiĂ© dans le presse-papier

Les types en Python

L'affichage montre que tous les types en Python sont des classes. Les entiers sont des objets de la classe int, les flottants sont des objets de la classe float, etc. Pour créer un entier ou une liste il suffit de les construire en utilisant le nom de leurs classes respectives

Recopier une par une les lignes suivantes, et les exécuter une par une.

🐍 Console Python
>>> type(int)
>>> type(float)
>>> entier = int()
>>> type(entier)
>>> ma_liste = list()
>>> type(ma_liste) 

Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activĂ©, le texte copiĂ© dans le terminal est joint sur une seule ligne avant d'ĂȘtre copiĂ© dans le presse-papier

Interface d'une classe

En définissant une classe on définit un type abstrait de données. Comme tout type abstrait, une classe possÚde donc une interface qui décrit l'ensemble des méthodes auxquelles on a accÚs pour manipuler les objets de cette classe.

On peut utiliser la fonction dir pour lister tous les attributs et méthodes d'un objet.

Taper dir(list) dans la console python pour visualiser les différentes méthodes et attributs de list.

Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activĂ©, le texte copiĂ© dans le terminal est joint sur une seule ligne avant d'ĂȘtre copiĂ© dans le presse-papier

Les méthodes du type prédéfini list

On retrouve ici les méthodes applicables sur les objets du type prédéfini list.

Son interface est disponible dans la documentation officielle

Vous noterez que l'interface ne précise pas la façon dont les méthodes sont implémentées mais juste la façon de les utiliser, ce qui suffit largement généralement.

On constate aussi qu'il y a de nombreuses méthodes dont le nom est encadré d'un double underscore __.
Ce sont des méthodes spéciales . Nous reviendrons sur quelques-unes d'entre elles un peu plus tard.