Programmation Orientée Objet
C3P
Vincent Aranega
[email protected]
Université de Lille
1/45
Table of contents
Programmation Orientée Objet ?
Programmation objet avec Python
Quelques points avancés
Tests unitaires
Structures de données
Exemples
Ressources
2/45
Table of Contents
Programmation Orientée Objet ?
Programmation objet avec Python
Quelques points avancés
Tests unitaires
Structures de données
Exemples
Ressources
3/45
C’est quoi la programmation orientée objet ?
Des idées ?
4/45
C’est quoi la programmation orientée objet ?
I Création d’artefacts “autonomes”
I Encapsulation
I Un objet représente un concept, une idée ou une entitée
I On met ensemble:
I les données
I les comportements associés
I L’état de l’application passe par les différents états des objets
I Paradigme de programmation
,→ il s’agit d’une façon de penser la construction d’un logiciel
basée sur certains principes fondamentaux et définis
5/45
Quelles conséquences ?
En pratique
I On créer des classes d’objets
I Un objet possède une structure interne et un comportement
I Un objet sait comment intéragir avec ses paires
En résumé
La programmation orientée objet met en avant la définition et
l’intéraction de briques logicielles appelées objets.
6/45
Histoire de Python
I Langage développé à par Guido van Rossum à partir de 1989
aux Pays-bas
I Recherche sur les systèmes d’exploitation :
I développé initialement pour simplifier la production de
programmes pour l’OS Amoeba
I typage dynamique
I langage hybride
1989 Guido débute le développement de Python
1991 Python 0.9 est rendu publique
1994 Python 1.0 est distribué avec des éléments fonctionnels
2000 Python 2.0 est distribué
2008 Python 3.0 est distribué
7/45
Points clé de la programmation objet ?
I Système de classe (et/ou méta-classes)
I Héritage
I Polymorphisme
I Encapsulation
Et pour Python ?
I Héritage multiple
I Modèle objet à base d’attributs
I Attributs (presque) toujours publique (rarement d’accesseurs)
I Duck typing
8/45
Table of Contents
Programmation Orientée Objet ?
Programmation objet avec Python
Quelques points avancés
Tests unitaires
Structures de données
Exemples
Ressources
9/45
Rappels généraux
I Pas de séparateurs d’instructions, on revient à la ligne.
I Les commentaires commencent par #.
I L’instruction print permet d’afficher n’importe quel objet
(fonction polymorphe).
I https://docs.python.org/3/tutorial/
introduction.html
a = 1
b = a + 4
a, b = b, a
if b == 5:
print(’Value is’, b)
else:
print("I don’t care")
Attention
Gestion des blocs par indentation
10/45
Rappels – list/tuple/dict
I Les listes sont mutables et peuvent contenir n’importe quel
type d’éléments.
I Les tuples sont immutables.
I Les dictionnaires permettent d’associer n’importe quel valeur à
une autre.
a = [1]
a.append(2) # a == [1, 2], a[0] vaut 1 et a[1] vaut 2
b = (1, 2, 3) # a[0] vaut 1, a[1] vaut 2 ...etc
c = {’a’: 2, ’b’: 3}
c[’c’] = 4 # c[’a’] vaut 2 ...etc
11/45
Rappels – boucles for
I Les boucles for itère sur des iterable .
I Beaucoup de type sont itérables (ex: les dicts, les tuples,
string...etc)
c = {’a’: 2, ’b’: 3, ’c’: 4}
for x in c:
print(x) # Iteration sur les clefs seulement
for k, v in c.items():
print(k, v) # Iteration sur les clefs valeurs
12/45
Création de classes
I La création de classe se fait avec le mot clef : class
I La méthode permettant d’init. les instances est __init__
I On accède à un attribut ou une fonction par la notation pointé.
class MaClass(object):
pass
class Point(object):
def __init__(self, x, y=0):
self.x = x
self.y = y
c1 = Point(x=3)
c2 = Point(x=3, y=4)
c3 = Point(4, 5)
print(c1.y) # affiche 0
print(c3.x) # affiche 4
13/45
Accès aux attributs
I Pas d’accesseurs ! En Python, on est entre adultes consentant
I Si on veut un attribut privé, la conventions veut qu’on le
préfixe avec un _
I Si on veut vraiment masquer un attribut, il faut préfixer avec
__ (il est rare de devoir avoir recours à ça en Python)
c1 = Point(x=3)
c2 = Point(y=4, x=3)
c3 = Point(4, 5)
print(c1.y) # affiche 0
print(c3.x) # affiche 4
14/45
Attributs dérivés
I possibilité d’avoir des attributs en lecture seule
I possibilité d’impacter la façon dont on lit ou écrit une donnée
class Person(object):
def __init__(self, birth_year):
self.year = birth_year
@property
def age(self):
import datetime; now = datetime.datetime.now()
return now.year - self.year
@age.setter
def age(self, new_age):
import datetime; now = datetime.datetime.now()
self.year = now.year - new_age
p = Person(2000)
print(p.age) # Declanche la methode ’age’
p.age = 30 # Declanche la methode ’age’ comme setter
p.year # 1989 si l’annee courante est 2019
15/45
Variables de classes
I Les variables de classes sont partagées par toutes les instances
I On accède aux variables depuis une instance ou directement
depuis la classe
class Point(object):
default_x = 5
default_y = 5
def __init__(self):
self.x = self.default_x
self.y = self.default_y
def addition(self, other):
return Point(self.x + other.x, self.y + other.y)
p1 = Point()
p2 = Point()
p2.x = 10
p3 = p1.addition(p2)
assert p1.default_x == Point.default_x
16/45
Méthodes statiques et méthodes des classes
Attention
I y a une différence entre méthode statique et méthode de classes
class Spam(object):
@classmethod
def egg(cls, i):
print("I know", cls, i)
@staticmethod
def bacon(i):
print("I only know", i)
def usage(self):
self.egg(4)
self.bacon(3)
Spam().usage()
Spam.egg(4)
Spam.bacon(5)
17/45
Classes et héritage multiple
I Python supporte l’héritage multiple
Figure: Problème du diamant
I Résolution par l’algorithme de linéarisation C3
18/45
Classes et héritage multiple
class A(object):
def display(self):
self.disp()
class B(A):
def disp(self):
print(’In B’)
class C(A):
def disp(self):
print(’In C’)
class D(B, C):
pass
D.mro() # returns (D, B, C, A)
d = D()
d.display() # print what?
19/45
Surcharge de méthodes
I La surcharge de méthode se fait uniquement par rapport au
nom de la méthode.
class A(object):
def spam(self):
cls_name = self.__class__.__name__
print(f"I’m in spam from A, called from {cls_name}")
class B(A):
def spam(self):
print("I’m in spam from B")
class C(A):
def spam(self):
super().spam()
print("Now I’m spam from C")
B().spam()
C().spam()
20/45
Typage dynamique et Duck Typing
I “If it walks like a duck and it quacks like a duck, then it must
be a duck”
I On considère des protocols (équivalent d’interfaces) mais qui
ne sont pas explicitement définis
class Duck(object):
def fly(self):
print("Duck flying")
class Airplane(object):
def fly(self):
print("Airplane flying")
class Whale(object):
def swim(self):
print("Whale swimming")
for animal in Duck(), Airplane(), Whale():
animal.fly()
21/45
Table of Contents
Programmation Orientée Objet ?
Programmation objet avec Python
Quelques points avancés
Tests unitaires
Structures de données
Exemples
Ressources
22/45
Dunder methods (ou magic methods)
I Il est possible de surcharger/réutiliser les opérateurs et
structures de base de Python
I Cela permet de donner des APIs plus fluide et simple à
manipuler
I ATTENTION : Il ne faut pas en abuser si ce n’est pas
nécessaire
class Point(object):
def __init__(self, x=5, y=5):
self.x = x
self.y = y
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
p1 = Point()
p2 = Point(y=6)
p3 = p1 + p2
23/45
Dunder methods – liste vraiment non exhaustive
__eq__(self, other) self == other
__pos__(self) +self
__neg__(self) -self
__invert__(self) ~self
__bool__(self) bool(self)
__getattr__(self, name) self.name
__setattr__(self, name, val) self.name = val
__getattribute__(self, name) self.name
__getitem__(self, key) self[key]
__setitem__(self, key, val) self[key] = val
__iter__(self) for x in self
__contains__(self, value) value in self
__call__(self [,...]) self(args)
24/45
Reflexion dans les langages
I Abilité d’un programme à s’examiner et à controler sa propre
implémentation
I La reflexion est composé de deux “techniques”:
1. l’instropection : capacité d’un programme à examiner son
propre état
2. l’intercession : capacité d’un programme à modifier son propre
état d’exécution ou d’altérer sa propre interprétation ou
signification
I Pourquoi est-ce utile ?
I Étendre un langage ou son design
I Construire des environnements de programmation
I Produire d’outils de développement avancés
I Produire des applications s’auto-modifiant ou
s’auto-optimisant
25/45
Accès reflexifs aux éléments d’un programme
I la fonction globals () retourne un dictionnaire des
modules/classes/fonctions/variables du scope global.
I la fonction locals () retourne un dictionnaire des
modules/classes/fonctions/variables du scope courrant.
I les éléments récupérés peuvent être utilisés directement
class A(object):
pass
def foo(o):
print(o)
locs = locals()
locs[’foo’](3)
myclass = locs[’A’]
instance = myclass()
locs[’A’] = type(’B’, [A], {’x’: 0})
instance2 = A()
print(instance2.x)
26/45
Accès reflexifs aux éléments d’un objet
I Le module inspect permet d’aider à l’inspection d’éléments
https://docs.python.org/3/library/inspect.html.
I il est possible d’utiliser la fonction vars (...) pour accéder aux
éléments d’un objet.
I la fonction dir (...) donne l’intégralité des méthodes
appelable depuis un élément.
I la dictionnaire __dict__ permet d’accéder à tout les
attributs explicite d’un élément.
I les méthodes sont des attributs que l’on peut appeler comme
des fonctions (ils sont callable).
I code. inspect permet d’accéder à tout les attributs d’un
élément.
I l’attribut __class__ permet d’accéder à la classe d’un objet.
27/45
Accès reflexifs aux éléments d’un objet
class A(object):
var1_int = None
var2_float = None
def printme(self):
print("I’m an object", self, "in", self.__class__)
print("var1", self.var1, "var2", self.var2)
def update_instance(inst, default):
from inspect import isfunction
for key, val in inst.__class__.__dict__.items():
if not isfunction(val) and not key.startswith(’__’):
varname, vartype = key.split(’_’)
new_val = default[vartype]
setattr(inst, varname, new_val)
a, b = A(), A()
update_instance(a, {’int’: 0, ’float’: 1.0})
update_instance(b, {’int’: 1234, ’float’: 45.3})
a.printme()
b.printme()
28/45
Compilation at run time
I Il est possible de compiler du code lors de l’exécution du code.
I Une fois compilé, le code peut-être directement utilisé.
def gen_getter_for_classvar(cls, attributes):
for name in attributes:
fun_name = "get_{}".format(name)
fun = """def {}(self):
return self.{}
""".format(fun_name, name)
loc, glob = {}, {}
exec(compile(fun, ’<string>’, ’exec’), glob, loc)
setattr(cls, fun_name, loc[fun_name])
class A(object):
x = 0
y = 0
gen_getter_for_classvar(A, ["x", "y"])
# Comment modifier pour l’appliquer a des instances ?
29/45
Typed Python
I Il est possible de typer explicitement les fonctions Python
I Les types sont conservé à l’exécution et ne sont pas analysé
par Python (on peut y accéder par introspection)
I Utilisation par des projets annexes pour founir de la validation
(ex: mypy)
class Point(object):
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def addition(self, other: Point) -> Point:
return Point(self.x + other.x, self.y + other.y)
30/45
Table of Contents
Programmation Orientée Objet ?
Programmation objet avec Python
Quelques points avancés
Tests unitaires
Structures de données
Exemples
Ressources
31/45
Tests avec pytest
I Il existe plusieurs framework de test en Python.
I PyTest est framework intéressant et très utilisés dans la
communauté même par de très gros projets
I Les tests reposent sur l’utilisation du mot clef assert .
def test__point_initialisation_without_args():
p = Point()
assert p.x == 5
assert p.y == 5
def test__point_initialisation_with_args():
p = Point(x=6)
assert p.x == 6
assert p.y == 5
p = Point(y=8)
assert p.x == 5
assert p.y == 8
32/45
Table of Contents
Programmation Orientée Objet ?
Programmation objet avec Python
Quelques points avancés
Tests unitaires
Structures de données
Exemples
Ressources
33/45
Pile à partir d’une liste
Les questions à se poser ?
I Quelles sont les entitées du système ?
I Quelles sont les méthodes que les entitées doivent gérer ?
I De manière générale, en OO, un diagramme de classe est
souvent pratique à utiliser
34/45
Pile à partir d’une liste
def test_stack_empty():
s = Stack()
assert s.is_empty() is True
def test_stack_push_peek():
s = Stack(); s.push(’a’); s.push(3)
assert s.peek() == 3
assert s.peek() == 3
def test_stack_push_pop():
s = Stack(); s.push(’a’); s.push(3)
assert s.pop() == 3
assert s.peek() == ’a’; assert s.pop() == ’a’
assert s.is_empty() is True
def test_exception():
s = Stack()
with pytest.raises(EmptyStack):
s.peek()
with pytest.raises(EmptyStack):
s.pop()
35/45
Pile à partir d’une liste
class EmptyStack(Exception): pass
class Stack(object):
def __init__(self, l=None):
self.inner_list = list(l) if l else []
def is_empty(self):
return self.inner_list == []
def peek(self):
try:
return self.inner_list[-1]
except IndexError:
raise EmptyStack()
def pop(self):
i = self.peek()
self.inner_list = self.inner_list[:-1]
return i
def push(self, item):
self.inner_list += [item]
36/45
Arbre binaire
I Utilisation du pattern composite
37/45
Arbre binaire
I Utilisation du pattern composite
class TreeElement(object):
def is_leaf(self): pass
def is_node(self): pass
class Node(TreeElement):
def __init__(self, value, left=None, right=None):
self.left = left
self.right = right
self.value = value
def is_leaf(self): ??
def is_node(self): ??
class Leaf(TreeElement):
def __init__(self, value=None):
self.value = value
def is_node(self): ??
def is_leaf(self): ??
38/45
Arbre binaire
Exercices
I Fonction d’affichage infix , prefix et postfix
I Fonction profondeur
I Fonction maximum
I Considérons arbre binaire de recherche, faire un fonction
insertion
39/45
Table of Contents
Programmation Orientée Objet ?
Programmation objet avec Python
Quelques points avancés
Tests unitaires
Structures de données
Exemples
Ressources
40/45
Exemples – Ruby
class Bibliotheque
attr_reader :livres
def initialize
@livres = []
end
def ajouter livre
raise "Already exist #{livre}" if @livres.include?
livre
@livres << livre
end
def taille
@livres.length
end
def auteurs
@livres.map { |livre| livre.auteurs }.flatten.uniq
end
end
41/45
Exemples – C#
public class Person {
private string m_name;
public string Name {
get { return m_name; }
set { m_name = value; }
}
public Person() { }
public Person(string name, ushort age) {
this.m_age = age;
this.m_name = name;
}
~Person() {
Console.WriteLine("Destruction");
}
public void SayHi() {
Console.WriteLine("Hello " + this.m_name);
}
} 42/45
Exemples – Smalltalk
43/45
Table of Contents
Programmation Orientée Objet ?
Programmation objet avec Python
Quelques points avancés
Tests unitaires
Structures de données
Exemples
Ressources
44/45
Ressources
Liens intéressants
I Le blog de Sam&Max une référence sur Python en France
(plutôt NSFW)
http://sametmax.com/
45/45