{Python}

Orienté Objet

# CHAPTER 1

La Programmation Orienté Objet

 

 

{Python}

  • Notions de base
  • Encapsulation
  • Héritage
  • Polymorphisme
  • Abstraction

Restant à voir

  • Aucun ¯\_(ツ)_/¯

Principes abordés

<

Les classes 😎

# PRESENTING CODE
class Robot():
    # une parfaite coquille vide, 
    # absolument inutile ...
    pass

Une classe est un plan, un patron, une description de l'objet à représenter

Les objets ☕🍺🪑

# PRESENTING CODE
class Robot():
    # une parfaite coquille vide, 
    # absolument inutile ...
    pass

# Oh la belle instance de classe ! 
# Naissance de votre premier objet
r = robot()

# Robot, fait des trucs !
r.do_something()

Un objet est l'instance d'une classe

Un objet est l'instance d'une classe

Un objet est manipulable

Une classe est une description

Vous connaissez déjà quelques objets

String

List

Tuple

Dictionnaire

s = "hello"
t = ('a','b','c','d','b','b')
l = ['a','b','c','d']
d = {'a':1, 'b':2, 'c':3, 'd':4}

# show some 'str' methods
str
isinstance(s, str)
s.upper()
s.capitalize()
s.join('_1_','_2_','_3_')

# show some 'tuple' methods
tuple
isinstance(t, tuple)
t.count('b')
t.index('c')

# show some 'list' methods
list
isinstance(l, list)
l.append('b')
l.sort()
l.pop()

# show some 'list' methods
dict
isinstance(d, dict)
d.keys()
d.values()
d.pop('b')

Attributs de classe

# PRESENTING CODE
class Robot():
    name = "<unnamed>"
    power = False
    current_speed = 0
    battery_level = 0
    speaches = {'boot':'Hello world', 'shutdown':'Goodbye world'}
    states = ['shutown', 'running']

Les attributs décrivent les informations internes ce que nous devons représenter

Les méthodes de classe

# PRESENTING CODE
class Robot():
    # les attibuts sont ici
    
    def boot(self):
      # Do someting to boot
      pass
      
    def shutdown(self):
      # Do someting to shutdown
      pass
      
    def charge(self):
      # Do someting to charge battery
      pass

Les méthodes sont les moyens d’interagir avec l'objet que nous devons représenter

What the self ?!

# PRESENTING CODE

self représente l'instance de la classe

self est passé explicitement à chaque méthode

Spécifique à Python

Dans la plupart des VRAI langages POO

le mot this est utilisé au lieu de self

this est une propriété interne à la classe qui désigne l'appel du futur objet

Constructeur 🔨 et Destructeur 💣

# PRESENTING CODE
ROBOT_COUNT = 0

class Robot():
    name = "<unnamed>"
    
    def __init__(self, name=None):
        if name:
            self.name = name
        global ROBOT_COUNT
        ROBOT_COUNT += 1
    
    def __del__(self):
      print("%s Auto destruction NOW"%(self.name))
      global ROBOT_COUNT
      ROBOT_COUNT -= 1

constructor_destructor.py

Les constructeurs / destructeurs sont

  • des méthodes de classe
  • automatiquement définits si non précisés
class Robot():
    name = "<unnamed>"
    power = False
    states = ['shutown','running']
    
    def __init__(self, name=None):
      if name:
        self.name = name
      self.current_state = self.states[0]
      self.power = False
      
# Python permet d'appeler directement la propriétés d'une classe
print(Robot.name)
print("Robot power is %s"%(Robot.power))
print(Robot.current_state)

# Oh la belle instance de classe : naissance d'un objet
r = Robot(name='Terminator')
print(r.name)
print("Robot power is %s"%(r.power))
print(r.current_state)

# Ceci n'a pas bougé
print(Robot.name)
print("Robot power is %s"%(Robot.power))
print(Robot.current_state)

Votre premier objet

On voit ici que current_state n'est appelable qu'après instanciation

constructor_destructor_and_object_attribut.py

# CHAPTER 1

La Programmation Orienté Objet

 

Les spécificités Objet Python

{Python}

Les fonctions pour faire joujou
avec les objets

class Robot():
  """I am a robot """
  
  name = 'Wall.E'
  
  def action(self):
    """ Turn the robot in action mode """
    pass

r = Robot()

# Pour comparer votre objet
isinstance(r, Robot)

# Récupérer la valeur d'un attribut
getattr(Robot, 'name')

# Pour comparer votre objet
callable(Robot().action)

callable(Robot().action())

dir(Robot)

help(Robot)

# Avec plus d'infos
from pprint import pprint
import inspect
pprint(inspect.getmembers(Robot))

for element in dir(Robot):
   if callable(getattr(Robot, element)):
       print(element, 'is method')
   else:
       print(element, 'is attribut')

built_in_class_utilities.py

Les méthodes spécifiques Python

 

__built-in__ ou __dunder__

Représenter votre objet

class Robot():
  def __init__(self, name, size):
    self.name = name
    self.size = size
  
  def __str__(self): # must return a string
    return "Robot %s [size: %s]"%(self.name, self.size)
  
  def __repr__(self): # must return a string
    return str({"name":self.name, "size":self.size})


r = Robot('Terminator', 'medium')

# __str__ call
print(str(r)) 
print(r) # print auto convert into string

# __repr__ call
print(repr(r))
r # in python command interpreter repr() is automatically called

built_in_class_object_display.py

Créer un itérateur de votre objet

class Robot():
  def __init__(self, data):
    self.data = data
    self.index = 0

  def __iter__(self):
    return self

  def __next__(self):
    if self.index >= len(self.data):
      raise StopIteration
    data = self.data[self.index]
    self.index += 1
    return data

    
r = Robot(['spam','foo','bar'])
for robot_data in r:
  print(robot_data)

built_in_class_methods.py

Les comparateurs

SIZES = {'small':1, 'medium':2, 'big':3}

class Robot():

  def __init__(self, size='medium'):
    self.size = SIZES[size]

  def __eq__(self, robot): # equal
    return (self.size == robot.size)

  def __ne__(self, robot): # not equal
    return (self.size != robot.size)

  def __gt__(self, robot): # greater than
    return (self.size > robot.size)

  def __ge__(self, robot): # greater or equal
    return (self.size >= robot.size)

  def __lt__(self, robot): # less than
    return (self.size < robot.size)

  def __le__(self, robot): # less or equal
    return (self.size <= robot.size)

    

built_in_class_methods.py

Il en existe en fait un paquet !

__setitem__
__getitem__
__delitem__
__len__
__contains__
__add__ / __iadd__
__sub__ / __isub__
__mul__ / __imul__
__truediv__ / __itruediv__
__call__

....

et bien d'autres :)

Pas d'exo, mais retenez bien l'existence de ces méthodes utilisées par Python qu'on appelle

__built-in__ ou __dunder__

  • Encapsulation
  • Héritage
  • Polymorphisme
  • Abstraction

Restant à voir

  • Notions de base

Principes abordés

La Programmation Orienté Objet

 

{Python}

<

L'encapsulation

Accesseurs et Mutateurs

  • Les accesseurs sont des méthodes qui permettent de retourner la valeur d’un attribut.

  • Les mutateurs permettent de modifier la valeur d’un attribut.

Votre premier concept Objet

class Robot():
    current_speed = 0
    
    # mutateur
    def stop(self):
      self.current_speed = 0
    
    # mutateur
    def move(self, speed):
      if type(speed) == int:
        self.current_speed = speed
    
    # accesseur
    def speed(self):
      return self.current_speed

Permet de masquer un certain nombre d’attributs et de méthodes afin de garder une cohérence dans la gestion de l’objet. Cela dissocie ce qui est interne à l’objet de ce qui est destiné à être utilisé par d’autres objets ou l’utilisateur

Attributs privés / publics

class Robot():
    # private attributes
    __name = '<unnamed>'
    __current_speed = 0
    # public attribute
    mission = '<unknown>'
    # protected attribute
    _secret = '42'
    
    # Simple style getter / setter
    def stop(self):
      self.__current_speed = 0
    
    def move(self, speed):
      if type(speed) == int:
        self.__current_speed = speed
      
    def speed(self):
      return self.__current_speed
    
    # Python style getter / setter 
    @property
    def name(self):
        return self.__name
    
    @name.setter
    def name(self,new_name):
        self.__name = new_name

encapsulation_private_public.py

protected

public

private

r = Robot()
r.name = "Termonator"
r.mission = "Kill them all"
r.move(100)
print(r.name + "\nSpeed:", r.speed())
print("Mission", r.mission)
print("Secret", r._secret)

IMPORTANT : En Python les notions de protected n'existent pas. Ce nommage est une convention pour inciter le développeur à ne pas utiliser cet attribut

double underscore

simple underscore

aucun underscore

Attributs privés / publics

Attention aux pièges de syntaxe

public

private

__foobar__

__foobar

foobar__

public

foobar

public

Attention aux pièges de syntaxe

piège courant

protected

public

_foobar

par convention

!!🚧

Protéger sa classe avec les __slots__

class Robot:
    # Empty class : 
    # nothing declared here
    pass

r = Robot()
r.name = 'Roger'
print(r.name)
r.hacked = '000101111001'
print(r.hacked)
class Robot:
    __slots__ = ('name', 'power')
    
    def __init__(self, name='', power=False):
        self.name = name
        self.power = power

r = Robot(name='Roger', power=False)
r.name = 'Super Roger'
print(r.name)
# Will throw an error
r.hacked = '000101111001'
print(r.hacked)

encapsulation_private_public_with_slots.py

Exoooooo 1

  1. Lorsque je crée mon robot, je veux pouvoir lui attribuer un nom
  2. Mon robot doit pouvoir s'allumer
  3. Mon robot doit pouvoir s'éteindre
  4. Mon robot doit pouvoir charger sa batterie à 100%, allumé ou non
  5. Le temps de charge ne peut pas être immédiat (10s max)
  6. Mon robot doit afficher sont % de batterie durant sa charge
  7. Mon robot doit pouvoir enregistrer une vitesse de déplacement
  8. Mon robot doit pouvoir me donner sa vitesse de déplacement
  9. Mon robot doit pouvoir arrêter son déplacement sur commande
  10. Mon robot doit pouvoir me fournir un résumé de son état global
  • Une fois terminé, commitez votre code sur Github
  • Je jouerai alors avec votre objet pour interagir avec lui
class Robot():
    __name = "<unnamed>"
    __power = False
    __current_speed = 0
    __battery_level = 0
    __states = ['shutown', 'running']
    
    
    """
      Give your best code here ( •̀ ω •́ )✧
    """

Classe de départ

exo1_starter_template.py

La Programmation Orienté Objet

 

{Python}

Les décorateurs Objet Python

@classmethod

class Robot():
    __name = 'Simple Robot'
    
    def __init__(self, name):
        self.__name = name
  
    # getter
    @property
    def name(self):
        return self.__name

    @classmethod
    def get_original_name(cls):
        return cls.__name
      
r = Robot('Roger le robot')

print(r.name)
print(r.get_original_name())

class_methods_decorators.py

@staticmethod

class Robot():
    __name = 'Simple Robot'
    
    def __init__(self, name):
        self.__name = name
  
    # getter
    @property
    def name(self):
        return self.__name

    @staticmethod
    def calculate(arg1, arg2):
        print('La réponse est %s'%(arg1+arg2))      


Robot.calculate(20+22)

class_methods_decorators.py

@annotations custom

def SecurisedAction(f):
    def decorator(*args): # on emploi parfois le nom 'wrapper'
        obj = args[0]
        if obj.safety_enabled:
            print('Robot is not allowed to do any action... ', str(f))
        else:
            return f(*args)
    return decorator

	
class Robot():
    __safety = False

    @property
    def safety_enabled(self):
        return self.__safety
    
    def enable_safety(self):
        self.__safety = True
        
    @SecurisedAction
    def fire(self):
        print('pew pew !')

    @SecurisedAction
    def run(self):
        print("let's goooo")

r = Robot()
r.run()
r.enable_safety()
r.fire()

decorators_bind_class_to_method.py

# CHAPTER 1

La Programmation Orienté Objet

 

Comparatif syntaxique

Python VS Java VS C++

{Python}

class Lamp {
  private boolean power;

  public void turnOn() {
    this.power = true;
  }

  public void turnOff() {
    this.power = false;
  }
  
  private void switch() {
    this.power = ! this.power;
  }
  
  public boolean isOn() {
    return this.power;
  }
}

class Main {
  public static void main(String[] args) {
    Lamp led = new Lamp();
    led.turnOn();
    if(led.isOn()){
      // do stuff
    }
  }
}
class Lamp:
  __power = False
  
  def turn_on(self):
    self.__power = True
  
  def turn_off(self):
    self.__power = False
  
  def __switch(self):
    self.__power = not self.__power
    
  def is_on(self):
    return self.__power

if __name__ == '__main__':
  led = Lamp()
  led.turn_on()
  if led.is_on(): 
    pass # do stuff

Python

Java

Python a une approche Uniform Access Principle. Les notions privé/publique sont donc moins intégrées au langage

class Lamp {
  private:
    bool power = false;
    
    private voir switch() {
       this.power = ! this.power;
    }

  public:
    void turnOn() {
      this.power = true;
    }

    void turnOff() {
      this.power = false;
    }

    bool isOn() {
      return this.power;
    }
};

int main() {
  Lamp led;
  led.turnOn();
  if(led.isOn()){
    // do stuff
  }
}

Python

C++

class Lamp:
  __power = False
  
  def turn_on(self):
    self.__power = True
  
  def turn_off(self):
    self.__power = False
  
  def __switch(self):
    self.__power = not self.__power
    
  def is_on(self):
    return self.__power

led = Lamp();
led.turn_on();
if led.is_on() : 
  pass # do stuff

Du fait que Python soit un langage scripté et interprété, il est le seul langage objet populaire à devoir passer sa propre instance via self (=this)

  • Héritage
  • Polymorphisme
  • Abstraction

Restant à voir

  • Notions de base
  • Encapsulation

Principes abordés

La Programmation Orienté Objet

 

{Python}

<

L’héritage permet, à partir d’une classe existante (parent), de définir une nouvelle classe (enfant) auquel on ajoute de nouveaux attributs et de nouvelles méthodes.

 

La classe enfant bénéficie dès sa définition de l'ensemble des attributs et méthodes public ou protected de la classe parent, mais pas les attributs et méthodes private.

L’héritage

Remote

Robot

operated

Automate

Unmanned

Autonomous

Unmanned

Unmanned

Unmanned

Q-UGV

Unmanned

UGV

Unmanned

UUV

Unmanned

UGV

Unmanned

UAV

L’héritage permet aussi de redéfinir les méthodes héritées de l’objet parent plutôt que d’en créer de nouvelles. Cette pratique s’appelle la redéfinition.

class GroundRobot(FieldTypeRobot):

    def __init__(self, name):
        super().__init__(name, 'ground')

class AirRobot(FieldTypeRobot):

    def __init__(self, name):
        super().__init__(name, 'air')

class UnderseaRobot(FieldTypeRobot):

    def __init__(self, name):
        super().__init__(name, 'undersea')

class SurfaceRobot(FieldTypeRobot):

    def __init__(self, name):
        super().__init__(name, 'surface')


ugv = GroundRobot('Wall.E')
uav = AirRobot('Megacopter')
usv = UnderseaRobot('Nautilus')
uuv = SurfaceRobot('Titaniktou')

ugv.status()
uav.status()
usv.status()
uuv.status()
from encapsulation_good_pratices import Robot

class FieldtypeRobot(Robot):
    __field = None

    def __init__(self, name, field):
        self.__field = field
        super().__init__(name)

    # redéfinition
    def __del__(self):
        pass # do not print message enymore !

    @property
    def field(self):
        return self.__field

    # redéfinition
    def status(self):
        print("%s (%s) status: %s [%s%% battery]"%(self.name, self.field, self.current_status, self.battery_level))

heritage.py

heritage.py

Toute classe de base Python hérite automatiquement de la classe object

# Les deux classes suivants sont équivalentes

class Robot():
    # héritage de 'object' implicite
    pass
  
class Robot(object):
    # héritage de 'object' explicite
    pass
  
dir(object)

L'héritage multiple et les __slots__ ne sont pas entièrement compatible

Héritage multiple

class Robot(Robot):
  	def charge(self):
    	pass

class WalkingRobot(Robot):   
    def walk(self):
        pass
  
class RidingRobot(Robot):   
    def ride(self):
    	pass

class Megatron(WalkingRobot, RidingRobot):   
	def transform(self):
        pass



mega = Megatron()

mega.walk()      # from WalingRobot
mega.transform() # from Megatron
mega.ride()		 # from RidingRobot
mega.charge()	 # from Robot

          depuis Python3.10

Multiple inheritance with multiple slotted parent classes can be used, but only one parent is allowed to have attributes created by slots (the other bases must have empty slot layouts) - violations raise TypeError.

 

Explications plus détaillées ici

Héritage multiple

Exoooooo 2

Une fois terminé, commitez votre code sur Github

  • Un Cyborg est un mélange d'humain et de robot
  • Un humain doit posséder un sexe (H / F)
  • Un humain doit pouvoir manger un ou plusieurs aliments
  • Un humain doit pouvoir digérer ce qu'il a mangé pas très important, faire en dernier si vous avez le temps
  • Réutiliser la class Robot faite dans l'exo 1. Sans copier/coller dans le fichier please ;)
  • Bonus : ajouter une méthode fun au Cyborg

on passe direct' à la pratique

créer un Cyborg

class Robot():
    # Robot class content here
    pass

class Human():   
    # Human class content here
    pass

class Cyborg(Robot, Human):   

    def __init__(self, name, sexe):   
        # initiate Robot parent class
        Robot.__init__(self, name)
        # initiate Human parent class
        Human.__init__(self, sexe)


cyborg = Cyborg('Deux Ex Machina', 'M')

print(cyborg.name, 'sexe', cyborg.sexe)
print('Charging battery...')
cyborg.charge()
cyborg.status()
cyborg.eat('banana')
cyborg.eat(['coca', 'chips'])
cyborg.digest()

Classe de départ

heritage_multi.py

L'héritage multiple

se définit ici

Exemple simple

class Robot():
    # Robot class content here
  	battery = 0
    
    def __init__(self, battery):
      self.battery = battery

    def charge(self):
      pass
      
class Human():   
    # Human class content here
    name = ''
    
    def __init__(self, name):
      self.name = name

    def breath(self):
      pass
class Cyborg(Robot, Human):   

    def __init__(self, name, battery):   
        # initiate Human parent class
        Human.__init__(self, name)
        # initiate Robot parent class
        Robot.__init__(self, battery)

	def night_vision(self):
      pass
    

cyborg = Cyborg('Deux Ex Machina', 100)

# utilisation des attributs publics de Human et Robot
print('Name', cyborg.name)
print('Battery level', cyborg.battery+'%')

# appel de méthode Human
cyborg.breath()
# appel de méthode Robot
cyborg.charge()
# appel de méthode Cyborg
cyborg.night_vision()

Héritage multiple

Exoooooo 2

Une fois terminé, commitez votre code sur Github

  • Un Cyborg est un mélange d'humain et de robot
  • Un humain doit posséder un sexe (H / F)
  • Un humain doit pouvoir manger un ou plusieurs aliments
  • Un humain doit pouvoir digérer ce qu'il a mangé pas très important, faire en dernier si vous avez le temps
  • Réutiliser la class Robot faite dans l'exo 1. Sans copier/coller dans le fichier please ;)
  • Bonus : ajouter une méthode fun au Cyborg

on passe direct' à la pratique

créer un Cyborg

Héritage en diamant

Human

Cyborg

Robot

FlyingRobot

FlyingCyborg

from pprint import pprint

class Human():
    pass

class Robot():
    pass

class FlyingRobot(Robot):
    pass

class Cyborg(Robot, Human):
    pass

class FlyingCyborg(FlyingRobot, Cyborg):
    pass

  
pprint(FlyingCyborg.mro())

""" Print output :

[<class '__main__.FlyingCyborg'>,
 <class '__main__.FlyingRobot'>,
 <class '__main__.Cyborg'>,
 <class '__main__.Robot'>,
 <class '__main__.Human'>,
 <class 'object'>]
"""

mro : method resolution order

heritage_resolution_order.py

La résolution d'un appel se fait suivant la règle du premier chemin directe vers la première classe parente par rapport à la résolution gauche-droite des héritages


La recherche dans une classe ne se fait qu’une fois. Une classe de la hiérarchie dans l’héritage des classes Python est donc parcourue qu’une seule fois

  • Polymorphisme
  • Abstraction

Restant à voir

  • Notions de base
  • Encapsulation
  • Héritage

Principes abordés

La Programmation Orienté Objet

 

{Python}

<

Le polymorphisme est un mécanisme important dans la programmation objet.

 

Il est la capacité d’un objet à posséder plusieurs formes. Ce principe découle de l’héritage.

 

Il permet de modifier le comportement d’une classe enfant par rapport à sa/ses classe(s) parent(s), en adaptant le comportement des objets suivant la redéfinition des méthodes.

Le polymorphisme

Le polymorphisme

class Robot():
    # Robot class content here
    pass

class Human():
    # Human class content here
    pass

class Cyborg(Robot, Human):
    # Cyborg class content here
    pass

cyb = Cyborg()

if issubclass(Cyborg, Human):
    print("Cyborg est une sous classe de Human !")

if issubclass(Cyborg, Robot):
    print("Cyborg est une sous classe de Robot !")

if isinstance(cyb, Human):
    print("Wesh frère t'es un mec un vrai !")

if isinstance(cyb, Robot):
    print("010110100101 101010100001 01010101110 !")

La reconnaissance du parent

polymorphism_relations.py

def use_robots_for_mission(robot: FieldTypeRobot) -> bool:
    """
        On attend ici un type FieldTypeRobot, ou tout autre classe hérité de FieldTypeRobot
    """
    robot.start_mission()

    try:
        robot.ride()
    except Exception as e:
        print(e, "  << mauvais appel, on est allé trop loin dans l'attente de l'objet")
    finally:
        print()


robots = [
    GroundRobot('Wall.E'),
    AirRobot('Megacopter'),
    UnderseaRobot('Nautilus'),
    SurfaceRobot('Titaniktou'),
]

for rob in robots:
    use_robots_for_mission(rob)

polymorphism_simple.py

Analysons ensemble un exemple un poil trapu...

Le polymorphisme

Ce principe est encore plus concret pour les langages typés

Python utilise le
duck typing

Le polymorphisme

class Robot{
    public void mission(){
        System.out.println("My mission is simple");
    }
}

class FlyingRobot extends Robot {
    public void mission(){
        System.out.println("My mission is aerial");
    }
}

class UnderwaterRobot extends Robot {
    public void mission(){
        System.out.println("My mission is underwater");
    }
}

class Main {
  public static void main(String[] args) {
    Robot rob = new Robot();
    rob.mission(); // as a Robot

    Robot frob = new FlyingRobot();
    frob.mission(); // as a FlyingRobot

    Robot urob = new UnderwaterRobot();
    urob.mission(); // as a UnderwaterRobot
  }
}

En Java

Pas d'exo, mais retenez bien le principe de polymorphisme via l'héritage

  • Abstraction

Restant à voir

  • Notions de base
  • Encapsulation
  • Héritage
  • Polymorphisme

Principes abordés

La Programmation Orienté Objet

 

{Python}

<

Les classes abstraites

Les classes abstraites permettent de définir par avance l'implémentation des méthodes des classes enfant.

 

Ainsi, on a l'assurance ses classes enfant auront bien le comportement défini et attendu par la classe parent abstraite.

 

Ce contrat est une interface de programmation.

 

Le module abc

from abc import ABCMeta

class Robot(metaclass=ABCMeta):
    # Abstract class concept 
    # can be used here
    pass
from abc import ABC

class Robot(ABC):
    # Abstract class concept 
    # can be used here
    pass

recommandé

Par héritage

Par méta classe

Abstract Base Classes

Le module abc Python permet d'accéder à la définition d'une classe abstraite

OU

/!\ obsolète /!\

Utiliser une classe abstraite

from abc import ABCMeta
from abc import abstractmethod

class TimeRobot(metaclass=ABCMeta):
    @property
    @abstractmethod
    def timezone(self):
        pass
    @classmethod
    @abstractmethod
    def default_timezone(cls):
        pass
    @staticmethod
    @abstractmethod
    def whattime():
        pass
    @timezone.setter
    @abstractmethod
    def timezone(self):
        pass
    @abstractmethod
    def mission(self):
        pass
import time

class Robot(TimeRobot):
    __timezone = 'Europe/London'
    @property
    def timezone(self):
        return self.__timezone 
    @classmethod
    def default_timezone(cls):
        return cls.__timezone
    @staticmethod
    def whattime():
        print('We are', time.asctime())
    @timezone.setter
    def timezone(self, timezone):
        self.__timezone = timezone
    def mission(self):
        print('Start Robot mission : travel in time...')

try: # Can not use TimeRobot as object !
    rob = TimeRobot()
except Exception as e:
    print(e)
    
rob = Robot()
rob.timezone = 'America/Montreal'
rob.mission()
Robot.whattime()

Tout est là

abstract.py

abstract.py

Il est impossible
d'instancier une classe abstraite

!!! ATTENTION !!!

@abstractmethod doit être annoté après les autres décorateurs

L'ordre est hyper important :

from abc import ABCMeta, abstractmethod

class Robot(metaclass=ABCMeta):

    @abstractmethod
    def start_mission(self):
        pass

class SuperRobot(Robot):
    pass

sr = SuperRobot()
sr.start_mission()

"""
Traceback (most recent call last):
  File "test.py", line 14, in <module>
    sr = SuperRobot()
TypeError: Can't instantiate abstract class 
  SuperRobot with abstract methods start_mission
"""
from abc import ABCMeta, abstractmethod

class Robot():

    @abstractmethod
    def start_mission(self):
        pass

class SuperRobot(Robot):
    pass

sr = SuperRobot()
sr.start_mission()

# OK, no error ¯\_(ツ)_/¯

Attention aux embrouille !

utilisation de @abstractmethod sans être une classe abstraite

abstract_test.py

abstract_test.py

Classes abstraites

Exoooooo 3

Une fois terminé, commitez votre code sur Github

  • En tant que client, je veux pouvoir jouer avec trois types de Véhicules différents : UUV, UAV, UGV
  • Seul les Véhicules finaux doivent être utilisables
  • Un Véhicules Unmanned doit exécuter une mission
  • Mettre en avant un principe de classe abstraite
  • Mettre en avant un principe de polymorphisme
  • Mettre en avant un principe d'héritage multiple
  • Pas d'algorithmes complexes, juste des print ;)

Sortez un bon vieux crayon pour schématiser vos dépendances d'héritages

place aux créateurs

from abc import ABCMeta, abstractmethod

""" You can use classes below or create your own 👍️"""

class UnmannedVehicule(metaclass=ABCMeta):
    """ 
        An autonomous vehicle have to do his mission automatically.
        This mission can be configured by an operator.
    """
    pass

class AerialVehicule(metaclass=ABCMeta):
    """ A vehicle made for aerial areas."""
    pass
class GroundVehicule(metaclass=ABCMeta):
    """ A vehicle made for ground fields."""
    pass
class UnderseaVehicule(metaclass=ABCMeta):
    """ A vehicle made for underwater sea."""
    pass

class UAV():
    """Unmanned Aerial Vehicule"""
    pass
class UUV():
    """Unmanned Undersea Vehicule"""
    pass
class UGV():
    """Unmanned Ground Vehicule"""
    pass

Classes de départ

heritage_multi.py

Aucun lien d'héritage n'est définit par défaut

La Programmation Orienté Objet

 

{Python}

Composer des classes

Garder à l'esprit qu'un objet peut être composé de plusieurs autres

class Leg():
  ...

class Arm():
  ...
  
class Head():
  ...
  
class Cyborg():
    def __init__(self):
        self.head = Head()
        self.left_leg = Leg()
        self.right_leg = Leg()
        self.left_arm = Arm()
        self.right_arm = Arm()

Il est important de savoir jouer entre composition et héritage

class Human():
    def __init__(self):
        self.head = Head()
        self.left_leg = Leg()
        self.right_leg = Leg()
        self.left_arm = Arm()
        self.right_arm = Arm()

    def walk(self, meters=0):
        for m in range(meters):
            self.right_leg.move()
            self.left_leg.move()

class Robot():
    def charge(self):
        print('Charging battery')

class Cyborg(Human, Robot):
    def __init__(self):
        Human.__init__(self)
        Robot.__init__(self)
        self.laser = Laser()
        self.rocket = Rocket()

cyb = Cyborg()
cyb.charge()
cyb.walk(meters=20)
cyb.laser.fire()
cyb.rocket.fire()
class Member(metaclass=ABCMeta):
    @abstractmethod
    def move(self): # moving action
        pass

class Leg(Member):
    def move(self):
        print('Do leg moving logic')

class Arm(Member):
    def move(self):
        print('Do leg moving logic')
  
class Head(Member):
    def move(self):
        print('Do head moving logic')
  
class Weapon(metaclass=ABCMeta):
    @abstractmethod
    def fire(self):
        pass
      
class Laser(Weapon):
    def fire(self):
        print('Zap zap zap !')
        
class Rocket(Weapon):
    def fire(self):
        print('BOOOOM !')

heritage_multi.py

La Programmation Orienté Objet

 

{Python}

Design Patterns

Designs patterns

Le plus complet

Le plus geek

Les sources de documentation qui vous aideront

Designs patterns

Les trois catégories

Patrons structurels

Patrons de création

Patrons comportementaux

<NOM/>

Abstract Factory

Singleton

Template Method

Decorator

Facade

Observer

Mediator

Proxy

Les plus utiles

<CATEGORIE/>

comportemental

création

création

structurel

structurel

comportemental

structurel

comportemental

Designs patterns

permet de créer des familles d’objets apparentés sans préciser leur classe concrète. La Factory utilise typiquement le principe de polymorphisme

Les plus utiles

Abstract Factory

Le client n’a pas besoin de connaître la classe concrète qu'il recevra de la Factory.

création

Singleton

Les plus utiles

L'utilisateur travaille toujours avec le même objet.

Permet de garantir l’unicité d’une instance pour une classe. En général pour contrôler l’accès à une ressource partagée..

Par exemple une Base de Données ou un fichier.

création

Permet de mettre une logique de structure dans la classe mère, mais laisse les sous-classes redéfinir la logique sans changer sa structure.

Les plus utiles

Template Method

Un plan architectural donné peut être légèrement remanié pour mieux répondre aux besoins d’un client.

Vous l'avez vu avec :

  • Classes abtraites
  • Méthodes abstraites

comportemental

Permet d’affecter dynamiquement de nouveaux comportements à des fonctions ou méthodes en les plaçant dans des emballeurs (wrappers) qui implémentent ces comportements.

Les plus utiles

Decorator

Les effets se cumulent si vous portez plusieurs couches de décorateurs.

Vous l'avez vu avec :

  • @property
  • @abstractmethod

structurel

Facade

Les plus utiles

Par analogie, lors d'une commande auprès d'un magasin, un opérateur joue le rôle de la façade pour l'ensemble des services du magasin

Permet de procurer une interface offrant un accès simplifié à une librairie, un framework ou à n’importe quel ensemble complexe logiciel.

Vous l'avez vu avec :

Le contexte __main__
... en quelque sorte.

structurel

Observer

Les plus utiles

Par analogie, lorsque vous vous inscrivez à une newsletter, vous n’avez plus besoin de vous rendre sur le site pour vérifier les actus. Le diffuseur vous envoie directement actus dès leur publication

Permet de mettre en place un mécanisme de souscription pour envoyer des notifications à plusieurs objets, au sujet d’événements concernant les objets qu’ils observent.

Evite ceci :

comportemental

Mediator

Les plus utiles

Par analogie, les appareils qui vont décoller ou atterrir sur dans un aéroport ne communiquent pas directement entre eux. Ils s’adressent à un Médiateur aérien (l'aéroport) qui orchestrera les communications et actions.

Permet de diminuer les dépendances chaotiques entre les objets. Il restreint les communications directes entre les objets et les force à collaborer uniquement via un objet médiateur.

comportemental

Proxy

Les plus utiles

Permet d’utiliser un substitut pour un objet. Elle donne le contrôle sur l’objet original, vous permettant d’effectuer des manipulations avant ou après que la demande ne lui parvienne.

Le proxy se déguise en objet base de données. Il peut gérer des actions et des données sans que le client ou que l’objet de la base de données ne le remarque.

Vous l'avez vu chez vous avec :

Votre VPN

 

structurel

{:Bonus:}

Les grandes batailles

de concepts

 Function prog. vs Object prog.

class RobotWheel():
    def move_forward(self):
        self.x_pos += 1
    def move_backward(self):
        self.x_pos -= 1
  
class Robot():
    def __init__(self):
        self.left_wheel = RobotWheel()
        self.right_wheel = RobotWheel()
    def move_forward(self):
        self.left_wheel.move_forward()
        self.right_wheel.move_forward()
    def move_backward(self):
        self.left_wheel.move_backward()
        self.right_wheel.move_backward()

# Main program infinite loop
if __name__ == '__main__':
  robot1 = Robot()
  robot2 = Robot()
  
  while my_great_students_do_not_sleep == True:
  	  robot1.move_forward()
  	  robot2.move_backward()
ROBOT1_ID = 'ROBOT1'
ROBOT2_ID = 'ROBOT2'
ROBOT_WHEELS_POSITION = {}

def move_robot_wheel_foward(robot_id):
    global ROBOT_WHEELS_POSITION
    ROBOT_WHEELS_POSITION[robot_id]['left'] += 1
    ROBOT_WHEELS_POSITION[robot_id]['right'] += 1
  
def move_robot_wheel_backard(robot_id):
    global ROBOT_WHEELS_POSITION
    ROBOT_WHEELS_POSITION[robot_id]['left'] -= 1
    ROBOT_WHEELS_POSITION[robot_id]['right'] -= 1

# Main program infinite loop
if __name__ == '__main__':
  ROBOT_WHEELS_POSITION[ROBOT1_ID] = {'left':0, 'right':0}
  ROBOT_WHEELS_POSITION[ROBOT2_ID] = {'left':0, 'right':0}
  
  while my_great_students_do_not_sleep == True:
      move_robot_wheel_foward(ROBOT1_ID)
      move_robot_wheel_foward(ROBOT2_ID)

Centralisé vs décentralisé vs distribué

Réseau banquaire

Crypto monaie

internet

Monolitic vs Microservices

Tout en un. une unique application

Découplage des

responsabilités entre applications

Références utilisées

Programmation Objet Python (la base et l'encapsulation)

 

Programmation Objet Python (héritage/polymorphisme)

 

Programmation Objet guides complets (plus ou moins)

Pour pratiquer et s'amuser

https://www.codingame.com/

https://www.codewars.com/

https://py.checkio.org/

02 - Python Orienté Objet

By Rémy Guillo du Bodan

02 - Python Orienté Objet

  • 827