Méthodologie de la Programmation
Notes de cours - Séance III

Objets

# On a vu que int, float, str et bool sont les types de base de Python # Mais dans Python tout est un objet d = {"Univ" : "Paris 8", "Niveau" : "L1"} l = [1, 5, 7, 11, 13] type(d) # <class 'dict'> type(l) # <class 'list'>

Création d’une classe

class Point(object): # Constructeur def __init__(self, x, y): self.x = x self.y = y point = Point(3, 4) print(point.x, point.y) # 3 4
# lister les noms d'un namespace print(dir(int)) # ['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']

Méthodes spéciales

class Point(object): # Constructeur def __init__(self, x, y): self.x = x self.y = y # Pretty-printer permettant d'utiliser print() sur un objet Point def __str__(self): return "<" + str(self.x) + "," + str(self.y) + ">"
point = Point(3, 4) print(point)

Utiliser les classes dans des fonctions

import random # Renvoie un point random dans le carré de diagonale [x, y] def point_random(xa, ya, xb, yb): x = random.randint(xa,xb) y = random.randint(ya, yb) return Point(x, y) # Renvoie le milieu du segment [P1, P2] def point_milieu(Pa, Pb): x = (Pa.x + Pb.x) // 2 y = (Pa.y + Pb.y) // 2 return Point(x, y)

Ajouter des méthodes à une classe

class Point(object): # Constructeur def __init__(self, x, y): self.x = x self.y = y # Pretty-printer permettant d'utiliser print() sur un objet Point def __str__(self): return "<" + str(self.x) + "," + str(self.y) + ">" def distance(self, destination): pass
point_a = Point(3, 4) point_b = Point(42, 50) print(point_a.distance(point_b)) # 60.3

Attention au scope des attributs de classe

class Animal(object): kind = 'chat' # attribut de classe partagé par toutes les instances global variable_globale # Ce truc très moche est aussi possible def __init__(self, name) : self.name = name # attribut d'instance unique à chaque instance def changeAnimal(self, newAnimal) : self.kind = newAnimal
animal_1 = Animal('foo') animal_2 = Animal('bar') print(Animal.kind) # chat print(animal_1.kind) # chat print(animal_2.kind) # chat Animal.kind = 'chien' animal_1.kind = 'cheval' print(Animal.kind) # chien print(animal_1.kind) # cheval print(animal_2.kind) # chien """ S'ils peuvent porter le même nom, les attibuts de classe et d'instance sont dans deux scopes distincts. Les méthodes regardent en priorité les attibuts d'instance en cas de surcharge des noms. """

Alias et copie d’objets

P = Point(3, 4) Q = P """ Ici on crée un alias, un nouveau nom, mais on ne copie pas l'objet. Mais lorsqu'on déclare Q, aucun nouvel objet n'est crée, celui-ci reçoit le même pointeur que P. """ # Pour vous en convaincre : P.x = 12 print(Q.x)
Q = Point(3, 4) P = Point(Q.x,Q.y) """ Par contre, de cette manière on crée bien une copie. L'objet Q et l'objet P ont des attributs de même valeurs, mais sont deux entités distinctes en mémoire. """

Classe Fraction

class Fraction(object): """ Un nombre représenté sous la forme d'une fraction """ def __init__(self, num, denom): """ Les numérateurs et dénominateurs sont des entiers. On verra une autre fois comment contrôler le typage des variables. """ self.num = num self.denom = denom def __str__(self): """ Pretty printer """ return str(self.num) + "/" + str(self.denom) def __add__(self, other): """ Retourne l'addition de self et d'une Fraction """ top = self.num*other.denom + self.denom*other.num bott = self.denom*other.denom return Fraction(top, bott) def __sub__(self, other): """ Retourne la soustraction de self et d'une Fraction """ top = self.num*other.denom - self.denom*other.num bott = self.denom*other.denom return Fraction(top, bott) def __mul__(self, other): """ Retourne la multiplication de self et d'une Fraction """ top = self.num * other.num bott = self.denom * other.denom return Fraction(top, bott) def __float__(self): """ Cast les valeurs de self en un seul float """ return self.num / self.denom def __eq__(self, other): """ Comparateur == """ return float(self) == float(other) def __neq__(self, other): """ Comparateur != """ return float(self) != float(other) def __lt__(self, other): """ Comparateur < """ return float(self) < float(other) def __le__(self, other): """ Comparateur <= """ return float(self) <= float(other) def __gt__(self, other): """ Comparateur > """ return float(self) > float(other) def __ge__(self, other): """ Comparateur >= """ return float(self) >= float(other) def inverse(self): """ Retourne 1 / self """ return Fraction(self.denom, self.num)
fa = Fraction(12,102) fb = Fraction(1,20) fc = Fraction(1,1) print(fa, fb, fc) # 12/102 1/20 1/1 print(fa + fb) # 342/2040 print(float(fa), float(fb), float(fc)) # 0.11764705882352941 0.05 1.0 print(fc > fa > fb) # True print(fa.inverse()) # 102/12 print(fc * Fraction(5,5)) # 5/5 print(fc == Fraction(200,200)) # True