Programmation orientée objet
Le paradigme OOP voit le programme comme un ensemble d'objets qui interagissent entre eux.
Classes, objets et héritage
- Chaque objet est défini par une classe qui elle même hérite d'autres classes ou interfaces.
- Une classe peut définir des propriétés et des méthodes, qu'on appelle des membres.
- Propriété : une vue sur une donnée via ses accesseurs en lecture et / ou écriture (qu'on appelle getters et setters respectivement).
- Méthode : fonction qui est définie au sein de la classe
- Constructeur : méthode particulière qui sera appelée automatiquement au moment de la création de l'instance
- En python, le premier argument des méthodes et du constructeur est une référence vers l'objet actuel
- Le nom de cet argument doit s'appeler
self
(appeléthis
dans d'autres langages) - On appelle
self
le contexte de la méthode
- Le nom de cet argument doit s'appeler
- Une classe enfant peut hériter d'une classe parente:
- Dans ce cas, la classe enfant contiendra implicitement tous les membres de la classe mère
- La classe enfant peut définir des membres supplémentaires qui lui seront propres
- La classe enfant peut redéfinir des membres de classe parent. On appelle cela une surcharge ou override en Anglais.
- Python est l'un des rares langages (avec le C++) à permettre l'héritage multiple. i.e. une classe peut hériter de plusieurs classes à la fois.
Définition d'une classe
class Rectangle:
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return (self.width + self.height) * 2
def __str__(self):
return f"x: {self.x}, ..."
r = Rectangle(10, 1, 100, 5)
print(r)
Héritage
class Person:
# constructeur
# self est l'équivalent du this dans les autres langages
def __init__(self, name, age):
self.name = name
self.age = age
def say_name(self):
print(self.name)
def __str__(self):
return f"Person: {self.name} - {self.age}"
class Student(Person):
def __init__(self, num_student, name, age):
self.num_student = num_student
# appel du constrcteur parent
super().__init__(name, age)
def __str__(self):
return f"Student {self.num_student}, Parent __str__:" + super().__str__()
pDico = {"name": "dfsdfs", "age": 230}
p1 = Person("John", 36)
print(p1.name)
print(p1.age)
p1.age = 10
p1.say_name()
print(p1, pDico)
s = Student("2323JFLSJ", "A", 19)
print(s)
Agrégation, composition et association
- L'agrégation et la composition représentent la relation avoir (l'héritage représente la relation être pour rappel)/
- L'agrégation est une relation entre deux classes où une classe contient une référence vers une autre classe.
- La composition est une relation plus forte que l'agrégation. Dans ce cas, la classe enfant ne peut pas exister sans la classe parente.
- L'association est une relation entre deux classes où une classe utilise l'autre sans en contenir une référence.
class Salary:
def __init__(self, pay):
self.pay = pay
def get_total(self):
return self.pay * 12
class Car:
pass
class House:
pass
class Computer:
def turn_on(self):
print("Turning on")
class Employee:
def __init__(self, pay, bonus, posessions):
self.bonus = bonus
# Composition
self.salary = Salary(pay)
# Agrégation
self.posessions = posessions
def compute_annual_salary(self):
return self.salary.get_total() + self.bonus
def work(self, computer):
"""Association entre Employee et Computer"""
computer.turn_on()
c1 = Car()
c2 = Car()
h = House()
emp = Employee(100, 10, [c1, c2, h])
print(emp.compute_annual_salary())
comp = Computer()
emp.work(comp)
UML
- UML (Unified Modeling Language) est un langage de modélisation graphique pour la conception de logiciels.
- Il permet de représenter les classes, les objets, les relations entre les objets, les cas d'utilisation, les séquences, etc.
Voici un exemple de diagramme de classe UML qui illustre l'héritage entre deux classes :
Deux classes qui héritent d'une classe
class SchoolPerson:
"""Classe de base ou une classe mère pour les classes Student et Teacher"""
def __init__(self, name, id, address):
self.name = name
self.id = id
self.address = address
def __str__(self):
return f"[SchoolPerson: {self.name}, {self.id}, {self.address}]"
class Student(SchoolPerson):
"""Un Student 'est' un SchoolPerson donc Student peut hériter de SchoolPerson"""
def __init__(self, name, id, address):
super().__init__(name, id, address)
class Teacher(SchoolPerson):
"""Un Teacher 'est' un SchoolPerson donc Teacher peut hériter de SchoolPerson"""
def __init__(self, name, id, address, contract_type):
super().__init__(name, id, address)
self.contract_type = contract_type
def __str__(self):
#return f"[Teacher: {self.name}, {self.id}, {self.address}, {self.contract_type}]"
return f"[Teacher: {self.contract_type}, ({super().__str__()})]"
s1 = Student("a", "12354S", "Home")
t1 = Teacher("b", "82546T", "Teacher Home", "Occasionnel")
print(s1, t1)
Voici un exemple de diagramme qui illustre l'association et la la composition :
Deux classes qui héritent d'une classe
import datetime
class LivingBeing:
def __init__(self, name, birth_date) -> None:
self.name = name
self.birth_date = birth_date
class Brain:
def __init__(self, volume) -> None:
self.volume = volume
class Hand:
def __init__(self, finger_count) -> None:
self.finger_count = finger_count
def do_thumbs_up(self):
print("👍")
class Heart:
pass
class Human(LivingBeing):
def __init__(
self, name, birth_date, social_security_number, height, heart, brain_volume
) -> None:
super().__init__(name, birth_date)
self.social_security_number = social_security_number
self.height = height
# Composition
self.brain = Brain(brain_volume)
self.hands = [Hand(3), Hand(12)]
# Agrégation
self.heart = heart
self.hp = 10
def walk(self, distance):
print(f"🚶 during {distance} KM")
def sleep(self, duration):
print(f"It's time to 😴 for {duration} minutes")
def talk(self, sentence):
print(f"🗣️: {sentence}")
def eat(self, fruit):
print(f"miam la pomme {fruit}")
fruit.weight = 1
self.hp += 10
def heal(self, human):
print(f"Soin appliqué à {human}")
human.hp += 3
def __str__(self) -> str:
return f"Human: hp -> {self.hp}"
class Fruit:
def __init__(self) -> None:
self.weight = 10
def __str__(self) -> str:
return f"Fruit: weight -> {self.weight}"
heart = Heart()
human1 = Human("Rémie", datetime.datetime(2003, 8, 15), 1_987_687_686, 175, heart, 2)
print("brain volume", human1.brain.volume, "M3")
human1.sleep(10)
human1.walk(2000)
human1.talk("hahaha")
a1 = Fruit()
a2 = Fruit()
human1.eat(a1)
human1.eat(a2)
h2 = Human("Dupont", datetime.datetime(2000, 8, 15), 100, 175, Heart(), 2)
human1.heal(h2)
print(human1, h2)
Propriétés, classes et méthodes statiques
- Propriété d'instance : chaque instance a ses propres propriétés d'instances
- Propriété statique : elle sera partagée entre toutes les instances (comme une variable globale pour la classe)
- Méthode d'instance : sera exécutée dans le contexte de l'instance qui l'a appelée (accessible via
self
) - Méthode statique : méthode qui a comme contexte que les propriétés et méthodes statiques de sa classe
- Classe statique : une classe qui ne peut pas être instanciée et ne contiendra donc que des propriétés et méthodes statiques
class Calculator:
PI = 3.14
@staticmethod
def add(x, y):
return x + y
@staticmethod
def multiply(x, y):
return x * y
@staticmethod
def compute_circle_area(r):
return r * r * Calculator.PI
print(Calculator.add(5, -8), Calculator.multiply(73, 0.8))
print(Calculator.PI, Calculator.compute_circle_area(8))
Propriétés, classes et méthodes abstraites
- Méthode abstraite: méthode qui n'a pas d'implémentation
- Propriété abstraite: propriété dont les accesseurs ne sont pas définis
- Classe abstraite: une classe qui a au moins une propriété ou méthode abstraite
- Les membres abstraits sont destinés à être définis par une sous-classe non abstraite.
from abc import ABC, abstractmethod
import random
class SchoolPerson(ABC):
def __init__(self, name, id, address):
self.name = name
self.id = id
self.address = address
def __str__(self):
return f"[SchoolPerson: {self.name}, {self.id}, {self.address}]"
@abstractmethod
def attend_class(self):
"""@abstractmethod rend l'instanciationd de cette classe impossible et oblige les sous-classes à implémenter la méthode"""
pass
class Student(SchoolPerson):
"""Un Student 'est' un SchoolPerson donc Student peut hériter de SchoolPerson"""
def __init__(self, name, id, address):
super().__init__(name, id, address)
def attend_class(self):
print("I learn")
class Teacher(SchoolPerson):
"""Un Teacher 'est' un SchoolPerson donc Teacher peut hériter de SchoolPerson"""
def __init__(self, name, id, address, contract_type):
super().__init__(name, id, address)
self.contract_type = contract_type
def __str__(self):
# return f"[Teacher: {self.name}, {self.id}, {self.address}, {self.contract_type}]"
return f"[Teacher: {self.contract_type}, ({super().__str__()})]"
def attend_class(self):
print("I teach")
s1 = Student("a", "12354S", "Home")
t1 = Teacher("b", "82546T", "Teacher Home", "Occasionnel")
print(s1, t1)
s1.attend_class()
t1.attend_class()