# Programmation orientée objet en Python ## Vocabulaire **Classe :** Structure qui contient un ensemble d'*attributs* et de *comportements*. Permet la création d'*instances*. *Hérite* des définitions de ses *superclasses*. **Instance :** Définie à partir d'une classe. Possède une copie *propre* des attributs et comprend les méthodes. **Methodes :** Fonctions applicables aux objets d'une classe. **Variables d'instance :** Attributs que chaque instance contient. Définies dans la classe. **Accesseurs :** Méthodes qui modifient l'état des variables d'instance. ## Traduction en Python ```python class Voiture(Vehicule): # CLASSE # CONSTRUCTEUR def __init__(self, numero, poids = 100, vitesse_max = 300): # VARIABLES D'INSTANCE self.poids = poids self.vitesse_max = vitesse_max self.numero = numero self.vitesse_actuelle = 0 self.constructeur = "Peugeot" def accelerer(self): self.vitesse_actuelle += 10 def ralentir(self): self.vitesse_actuelle -= 10 # Acces aux variables voiture_1 = Voiture(1) print(voiture_1.poids) >>> 100 print(voiture_1.vitesse_actuelle) >>> 0 voiture_1.accelerer() voiture_1.accelerer() print(voiture_1.vitesse_actuelle) >>> 20 ``` ## Question 1 : Définition de la population d'insectes ### Éléments de réponse - **Surcharge de `str()`:** Vous pouvez redéfinir l'affichage de `print()` pour votre objet en définissant la méthode `__str(self)__` dans votre classe. - **Formattage de chaines de caractères :** ```python "Des chaines peuvent etre incluses successivement \ avec {0}, {1} et {2}".format(chaine0, chaine1, chaine2) ``` ```python f"Des chaines peuvent etre incluses successivement \ avec {chaine0}, {chaine1} et {chaine2}" ``` - **Squelette de départ:** ```python from numpy.random import randint import sys if __name__ == "__main__": ecosys = [] nbins = int(sys.argv[1]) nbtour = int(sys.argv[2]) for i in range(nbins): ecosys.append(Insecte(randint(-10, 11), randint(-10, 11), randint(3, 8))) for t in range(nbtour): print("### Tour %i ###"%(t)) for ins in ecosys: ins.unTour() print(ins) ``` ### Correction ```python class Insecte(): def __init__(self, abscisse, ordonnee, capacite=20): self.sante = randint(capacite//2, capacite) self.max = capacite self.coords = abscisse, ordonnee def __str__(self): return "{} : position ({}, {}) sante {}/{}".format( self.car(), self.coords[0], self.coords[1], self.sante, self.max ) def car(self): return 'I' def unTour(self): ''' Fait passer l'instecte au pas suivant, en mettant à jour leur santé et leur position. ''' self.__sante -= 1 if self.__sante==0: self.__sante = self._max self.coords = (self.coords[0]+randint(-3,3), self.coords[1]+randint(-3,3)) ``` ## Question 2 : Placement d'une cage ### Éléments de réponse - **Variables privées :** Préfixer une variable avec `__` la rend innaccessible directement. Par exemple faire `insecte.__sante()` renvoie une erreur. - **Décorateurs pour accesseurs :** Pour protéger les variables privées on surveille leur accès avec des accesseurs (communément `getter` et `setter`). Sans décorateurs: ```python def get_x(self): return self.x def set_x(self, new_x): self.x = new_x obj.set_x(3) obj.get_x() >>> 3 ``` Avec décorateurs: ```python @property def x(self): return self.x @x.setter def x(self, new_x): self.x = new_x obj.x = 3 obj.x >>> 3 ``` <!-- ### Correction ```python class Insecte: ... @property def coords(self): return self.__coords @coords.setter def coords(self, nouv_coord): x, y = nouv_coord x = min(x, 30) x = max(x, 0) y = min(y, 20) y = max(y, 0) self.__coords = (x, y) @property def x(self): return self.coords[0] @property def y(self): return self.coords[1] def unTour(self): ''' Fait passer l'instecte au pas suivant, en mettant à jour leur santé et leur position. ''' self.__sante -= 1 if self.__sante==0: self.__sante = self._max self.coords = (self.x+randint(-3,3), self.y+randint(-3,3)) def __str__(self): ''' Representation textuelle d'un insecte. ''' return "%c : position (%i, %i) etat %i/%i"%( self.car(), self.x, self.y, self.__sante, self._max ) ``` --> ## Question 3 : Distinction des fourmis et cigales ### Éléments de réponse - **Superclasses, sous-classes et héritage :** Pour conserver le comportement défini dans une classe, on procède à un héritage ```python class Voiture(Vehicule): ... # Pour appeler une méthode de "Vehicule" super().methode_de_vehicule() class Camion(Vehicule): ... # Pour appeler une méthode de "Vehicule" super().methode_de_vehicule() ``` - **Surcharge de méthode :** Pour changer le comportement d'une méthode déja définie dans la superclasse, il faut **surcharger** (redéfinir la méthode) dans la nouvelle classe. ```python class Vehicule(): ... def methode(self): print("vehicule") class Voiture(Vehicule): ... def methode(self): print("voiture") class Camion(Vehicule): ... vehicule = Vehicule() voiture = Voiture() camion = Camion() vehicule.methode() voiture.methode() camion.methode() >>> "vehicule" >>> "voiture" >>> "vehicule" ``` <!-- ### Correction ```python class Animal(): ... @property def sante(self): return self.__sante @sante.setter def sante(self, value): if value <= self._max: self.__sante = value # Verification en cas de valeur négative if value <= 0: value = 0 ... class Fourmi(Animal): def car(self): return 'F' class Cigale(Animal): def __init__(self, x, y): """Le constructeur de la classe Cigale. Précision du constructeur d'Animal en passant la sante actuelle au max x, y : int. """ super().__init__(x, y) self.sante = self._max def car(self): return 'C' ``` --> ## Question 4 : Action des insectes ### Éléments de réponse - **Actions des insectes :** Changement de `unTour()` en: ```python class Animal(): ... def unTour(self): self.manger() self.bouger() ``` - Précision des nouvelles actions pour `Fourmi` et `Cigale`: `manger()`: - **Communes** : Baisse de santé - **`Fourmi`** : Si la santé est trop faible, remise au max - **`Cigale`** : Trois actions, choix au hasard `bouger()`: - **Communes** : Déplacement <!-- ### Correction ```python class Animal(): ... def bouger(self): self.coords = (self.x+randint(-3,4), self.y+randint(-3,4)) def manger(self): self.sante -= 1 class Fourmi(Animal): ... def manger(self): super().manger() if self.sante<=3: self.sante = self._max class Cigale(Animal): ... def manger(self): super().manger() action = randint(3) if action == 1: print("Je danse") elif action == 2: print("Je chante") elif action == 0: print("Je mange") if self.sante<=2: self.sante = self._max # Vérification du niveau de santé if self.sante<=0: print("Je meurs de faim") ``` --> ## Question 5 : Création de l'objet `Ecosysteme` ### Éléments de réponse - **Création de modules :** Garder une classe dans son fichier respectif facilite la hiérarchie. Pour utiliser une classe définie ailleurs il faut l'importer. Ici nos fichiers `insectes.py` et `ecosysteme.py` sont définis et on peut importer les insectes dans l'écosystème avec: ```python from insectes import Fourmi, Cigale ``` - **Mise en place de l'écosystème :** L'écosystème est une sous-classes de la liste classique de Python et peut se définir comme : ```python class Ecosysteme(list): ... ``` <!-- ### Correction ```python class Ecosysteme(list): """ Classe gérant le déroulement du jeu. """ def __init__(self, nb_ins,nbt,xmax,ymax): self.__xmax = xmax self.__ymax = ymax self.nbtour = nbt for i in range(nb_ins): if randint(0, 2)==0: self.append(Fourmi(randint(0, xmax), randint(0, ymax), self)) else: self.append(Cigale(randint(0, xmax), randint(0, ymax), self)) @property def dims(self): """ Renvoies les dimensions du plateau de jeu """ return (self.__xmax, self.__ymax) def __str__(self): """Affiche le plateau de jeu en mode texte suivant les codes couleur définis dans les sous classes de ''Terrain'' et les caractères définis dans les sous classes de ''Animal''. """ # decommenter la ligne choisie : #return self.strCol() # Pour l'affichage en couleur return self.str2() # Pour l'affichage sur deux caractères def unTour(self): """ Effectue toutes les actions liées à un tour de jeu. """ for ins in self: ins.bouger() ins.manger() def simuler (self): """ Contrôle l'évolution du jeu, affiche le résultat de chaque tour dans un terminal. """ for t in range(self.nbtour): print("### Tour %i ###"%(t)) self.unTour() print(self) time.sleep(0.2) if __name__ == "__main__": nbins = 12 nbtour = 43 ecosys = Ecosysteme(nbins,nbtour,20,15) print(ecosys) ecosys.simuler() ``` --> ```python def mouvNour(self): v, xmin, ymin = self._eco.vue(self.x, self.y, 4) liste_nour = [] for i in range(len(v)): for j in range(len(v[0])): if v[i][j] == 1: cx = i + xmin cy = j + ymin d = max(abs(cx - self.x), abs(cy - self.y)) liste_nour.append((d, cx, cy)) if liste_nour == []: self.mouvAlea() else: np.random.shuffle(liste_nour) liste_nour.sort() objx, objy = liste_nour[0][1:] self.coords = (self.x + sign(objx - self.x), self.y + sign(objy - self.y)) ``` --> ```python def strCol(self): """ Conversion en chaîne pour affichage sur terminal bash en couleur """ pos = {} for ins in self: pos[ins.coords]=ins.car() s = "" for i in range(self.__xmax): for j in range(self.__ymax): if i%5==0 and j%5==0: s += "\x1b[102;31m" else: s += "\x1b[43;31m" if (i, j) in pos: s += pos[(i,j)] else: s += "." s += "\x1b[0m\n" return s def str2(self): """ Conversion en chaîne avec deux caractères par case. """ pos = {} for ins in self: pos[ins.coords]=ins.car() s = "" for i in range(self.__xmax): for j in range(self.__ymax): if i%5==0 and j%5==0: s += "#" else: s += "." if (i, j) in pos: s += pos[(i,j)] else: s += " " s += "\n" return s ``` ###### tags: `python`