Au commencement étaient les variables
x = 27
Elles représentaient parfois des concepts sophistiqués
import math
point_1 = (27, 13)
point_2 = (19, 84)
def distance(p1, p2):
return math.sqrt((p2[0]-p1[0])**2+(p2[1]-p1[1])**2)
distance(point_1, point_2)
Et c'était pénible à écrire et à comprendre
Pour simplifier, on peut nommer les données contenues dans variables, par exemple avec un dict
point_1 = {'x': 27, 'y': 13}
point_2 = {'x': 19, 'y': 84}
def distance(p1, p2):
return math.sqrt((p2['x']-p1['x'])**2+(p2['y']-p1['y'])**2)
distance(point_1, point_2)
Et c'est toujours aussi pénible à écrire mais un peu moins à lire
On peut avoir une syntaxe plus agréable en utilisant des tuples nommés
from collections import namedtuple
Point = namedtuple('Point', ('x', 'y'))
point_1 = Point(27, 13)
point_2 = Point(19, 84)
def distance(p1, p2):
return math.sqrt((p2.x-p1.x)**2+(p2.y-p1.y)**2)
distance(point_1, point_2)
Voilà, le cours est fini, bonnes vacances.
namedtuple
sont ce qu'on appelle des enregistrements (en C des structs)Vecteur = namedtuple('Vecteur', ('x', 'y'))
v1 = Vecteur(27, 13)
v2 = Vecteur(1, 0)
def norm(v):
return math.sqrt(v.x**2 + v.y**2)
def is_unit(v):
return norm(v) == 1
print(is_unit(v1))
print(is_unit(v2))
C'est plutôt lisible
Mais si je veux pouvoir faire aussi de la 3d
Vecteur3D = namedtuple('Vecteur3D', ('x', 'y', 'z'))
u1 = Vecteur3D(27, 13, 6)
u2 = Vecteur3D(1, 0, 0)
def norm3d(v):
return math.sqrt(v.x**2 + v.y**2 + v.z**2)
def is_unit3d(v):
return norm3d(v) == 1
print(is_unit3d(u1))
print(is_unit3d(u2))
C'est affreusement pénible de réécrire comme ça le même code.
Une autre solution
def norm(v):
if isinstance(v, Vecteur3D):
return math.sqrt(v.x**2 + v.y**2 + v.z**2)
elif isinstance(v, Vecteur):
return math.sqrt(v.x**2 + v.y**2)
else:
raise ValueError('Type non supporté')
def is_unit(v):
return norm(v) == 1
print(is_unit(v1))
print(is_unit(u2))
C'est un peu mieux mais pas top. (Même si on aurait pu trouver une solution plus intelligente)
Une des solutions pour faire mieux c'est de passer à la vitesse supérieure : les objets.
Ça va d'abord être un peu plus désagréable, pour ensuite être beaucoup plus agréable.
class Vecteur:
def __init__(self, x, y):
self.x = x
self.y = y
def norm(self):
return math.sqrt(self.x**2 + self.y**2)
v1 = Vecteur(27, 13)
v2 = Vecteur(1, 0)
print(v1.norm())
print(v2.norm())
class Vecteur3D:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def norm(self):
return math.sqrt(self.x**2 + self.y**2 + self.z**2)
u1 = Vecteur3D(27, 13, 6)
u2 = Vecteur3D(1, 0, 0)
print(u1.norm())
print(u2.norm())
def is_unit(v):
return v.norm() == 1
print(is_unit(v1))
print(is_unit(u2))
Le choix de la bonne fonction norme
se fait automagiquement (on appelle ça du polymorphisme)
Résumons
Et vous en avez déjà rencontré plein
print(type('abc'))
print('abc'.islower())
Car en Python, tout est objet. Ce qui ne veut pas dire qu'on est obligé d'y faire attention…
La programmation orientée objet (POO) est une manière de programmer différente de la programmation procédurale vue jusqu'ici.
C'est une façon particulière de résoudre les problèmes, on parle de paradigme, et il y en a d'autres
Python fait partie des langages multi-paradigmes : on utilise le plus pratique, ce qui n'est pas sans déplaire aux puristes mais
« We are all consenting adults here »
class
class Word:
""" Classe Word : définit un mot de la langue """
pass
Pour créer un objet, on appelle simplement sa classe comme une fonction
word1 = Word()
print(type(word1)) # renvoie la classe qu'instancie l'objet
On dit que word1
est une instance de la classe Word
Et il a déjà des attributs et des méthodes
word1.__doc__
print(dir(word1))
Et aussi un identifiant unique
id(word1)
word2 = Word()
id(word2)
Il existe une méthode spéciale __init__()
qui automatiquement appelée lors de la création d'un objet. C'est le constructeur
Le constructeur permet de définir un état initial à l'objet, lui donner des attributs par exemple
Les attributs dans l'exemple ci-dessous sont des variables propres à un objet, une instance
class Word:
""" Classe Word : définit un mot de la langue """
def __init__(self, form, lemma, pos):
self.form = form
self.lemma = lemma
self.pos = pos
word = Word('été', 'être', 'V')
word.lemma
word2 = Word('été', 'été', 'NOM')
word2.lemma
self
leur premier paramètre, qui fera référence à l'objet lui-même.class Word:
""" Classe Word : définit un mot simple de la langue """
def __init__(self, form, lemma, pos):
self.form = form
self.lemma = lemma
self.pos = pos
def is_inflected(self):
if self.form != self.lemma:
return True
else:
return False
w = Word('orientales', 'oriental', 'adj')
w.is_inflected()
Pourquoi self
? Parce que écrire w.is_inflected()
c'est du sucre pour
Word.is_inflected(w)
class Cake:
""" un beau gâteau """
def __init__(self, farine, oeuf, beurre):
self.farine = farine
self.oeuf = oeuf
self.beurre = beurre
def is_trop_gras(self):
if self.farine + self.beurre > 500:
return True
else:
return False
def cuire(self):
return self.beurre / self.oeuf
Cake est la classe mère.
Les classes enfants vont hériter de ses méthodes et de ses attributs.
Cela permet de factoriser le code, d'éviter les répétitions et les erreurs qui en découlent.
class CarrotCake(Cake):
""" pas seulement pour les lapins
hérite de Cake """
carotte = 3
def cuire(self):
return self.carotte * self.oeuf
class ChocolateCake(Cake):
""" LE gâteau
hérite de Cake """
def is_trop_gras(self):
return False
gato_1 = CarrotCake(200, 3, 150)
gato_1.is_trop_gras()
>>> False
gato_1.cuire()
>>> 9
gato_2 = ChocolateCake(200, 6, 300)
gato_2.is_trop_gras()
>>> False