Classe, Héritage, calcule d’une distance¶
On veut calculer la distance entre un produit et un utilisateur. En première intention, la distance entre deux produits est calculée en fonction de ses attributs et la distance entre deux utilisateurs est calculée en fonction de leurs achats. La pertinence de la distance n’est pas le sujet ici mais quoi coder et dans quelle classe.
Un peu de vocabulaire :
une méthode est une fonction d’une classe
un attribut est une variable d’une classe
le constructeur ou la méthode
__init__
est appelé implicitement lorsque l’objet est créé
Classe Product¶
On lui adjoint trois attributs, un identifiant, un prix, une category.
[9]:
class Product:
def __init__(self, identifiant, prix, category):
self.identifiant = identifiant
self.prix = prix
self.category = category
def __repr__(self):
return f"P:{self.identifiant}:{self.prix}:{self.category}"
# def __eq__(self, p):
# # Cet méthode définit le sens de l'opérateur ==.
# # Par défaut, python vérifie que les deux variables comparées désignent
# # la même instance et non leur contenu.
# return self.identifiant == p.identifiant
def distance(self, p):
return abs(self.prix - p.prix) * (1 if self.category == p.category else 10)
p = Product(1, 45, "livre")
p
[9]:
P:1:45:livre
[10]:
p1 = Product(1, 1, 1)
p2 = Product(1, 1, 1)
p3 = p1
p1 == p2, p1 == p3
[10]:
(False, True)
Classe utilisateur¶
[11]:
import numpy as np
class User:
def __init__(self, identifiant):
self.identifiant = identifiant
self.products = []
def bought(self, p):
self.products.append(p)
def __repr__(self):
return f"U:{self.identifiant}:{len(self.products)}"
def distance(self, user):
# distance entre self et user
mat = np.empty((len(self.products), len(user.products)), dtype=float)
for i, p1 in enumerate(self.products):
for j, p2 in enumerate(user.products):
mat[i, j] = p1.distance(p2)
return min(mat.min(axis=1).sum(), mat.min(axis=0).sum())
u1 = User(11)
u1.bought(Product(1, 45, "livre"))
u2 = User(12)
u2.bought(Product(1, 40, "livre"))
u2.bought(Product(1, 30, "disque"))
u1.distance(u2)
[11]:
5.0
Classe Base¶
La distance calculée sur les utilisateurs peut être calculée de la même façon sur les produit si la classe Product
sait quels utilisateurs les ont acheté. De cette façon, on peut dire que deux produits sont proches s’ils sont achetés par les mêmes utilisateurs. Comme ce calcul est le même, il est tentant de créer une classe commune aux produits et aux utilisateurs. Les classes Product
et User
vont hériter de cette nouvelle classe.
On distance donc :
la méthode
distance
qui est la distance décrite ci-dessus,la méthode
similarity
qui compare deux produits à partir de leurs attributs.
Les instructions print
permettent de suivre quelle méthode est appelée.
[12]:
class Base:
def __init__(self, identifiant):
self.identifiant = identifiant
self.container = []
def add(self, b):
self.container.append(b)
def similarity(self, b):
# Déclencher une exception NotImplementedError est une façon de dire
# que la méthode existe mais qu'elle doit être redéfinit ou surchargée dans la
# classe qui hérite de la classe Base.
raise NotImplementedError(
f"il faut surcharger cette méthode pour le type {type(self)}"
)
def distance(self, user):
# distance entre self et user
print(f"Base:distance:{self.identifiant}:{user.identifiant}")
mat = np.empty((len(self.container), len(user.container)), dtype=float)
for i, p1 in enumerate(self.container):
for j, p2 in enumerate(user.container):
mat[i, j] = p1.similarity(p2)
return min(mat.min(axis=1).sum(), mat.min(axis=0).sum())
# Maintenant les héritages
class Product(Base):
def __init__(self, identifiant, prix, category):
Base.__init__(self, identifiant)
self.prix = prix
self.category = category
def similarity(self, p):
# On redéfinit la méthode similarity dans la classe Product car elle lui est propre.
print(f"Product:similarite:{self.identifiant}:{p.identifiant}")
return abs(self.prix - p.prix) * (1 if self.category == p.category else 10)
class User(Base):
def bought(self, p):
self.add(p)
def similarity(self, u):
# On redéfinit la méthode similarity de sorte qu'elle appelle la méthode distance.
# car elle est appelée par la méthode Product.distance.
print(f"User:similarity:{self.identifiant}:{u.identifiant}")
return self.distance(u)
p1 = Product(1, 45, "livre")
p2 = Product(2, 40, "livre")
p3 = Product(3, 30, "disque")
u1 = User(11)
u1.bought(p1)
u2 = User(12)
u2.bought(p2)
u2.bought(p3)
p1.add(u1)
p2.add(u2)
p3.add(u2)
Toujours la distance entre deux utilisateurs.
[13]:
u1.distance(u2)
Base:distance:11:12
Product:similarite:1:2
Product:similarite:1:3
[13]:
5.0
Et maintenant la distance entre deux produits définie à partir des utilisateurs ayant acheté des produits.
[14]:
p1.distance(p2)
Base:distance:1:2
User:similarity:11:12
Base:distance:11:12
Product:similarite:1:2
Product:similarite:1:3
[14]:
5.0
[ ]: