# 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`