Merge pull request #11 from freezed/dico-ord

Fin du TP «Un dictionnaire ordonné»
This commit is contained in:
Fred Z 2018-02-05 18:08:43 +01:00 committed by GitHub
commit 9952eae898
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 740 additions and 0 deletions

202
3.7-DicoOrd.py Normal file
View File

@ -0,0 +1,202 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Author: freezed <freezed@users.noreply.github.com> 2018-01-25
Version: 0.1
Licence: `GNU GPL v3`: http://www.gnu.org/licenses/
"""
class DictionnaireOrdonne:
"""
Dictionnaire ordonne
====================
Objet ressemblant a un dict, avec des capacitees de tri.
Les cles et valeurs se trouvant dans des listes de meme
taille, il suffira de prendre l'indice dans une liste pour
savoir quel objet lui correspond dans l'autre. Par exemple,
la cle d'indice 0 est couplee avec la valeur d'indice 0.
On doit pouvoir ajouter deux dictionnaires ordonnes
(dico1 + dico2) ; les cles et valeurs du second dictionnaire
sont ajoutees au premier.
:Example:
>>> fruits = DictionnaireOrdonne()
>>> fruits
{}
>>> fruits["pomme"] = 52
>>> fruits["poire"] = 34
>>> fruits["prune"] = 128
>>> fruits["melon"] = 15
>>> fruits
{'pomme': 52, 'poire': 34, 'prune': 128, 'melon': 15}
>>> fruits.sort()
>>> print(fruits)
{'melon': 15, 'poire': 34, 'pomme': 52, 'prune': 128}
>>> legumes = DictionnaireOrdonne(carotte = 26, haricot = 48)
>>> print(legumes)
{'carotte': 26, 'haricot': 48}
>>> len(legumes)
2
#>>> legumes.reverse()
#>>> fruits = fruits + legumes
#>>> fruits
#{'melon': 15, 'poire': 34, 'pomme': 52,
#'prune': 128, 'haricot': 48, 'carotte':26}
#>>> del fruits['haricot']
#>>> 'haricot' in fruits
#False
#>>> legumes['haricot']
#48
#>>> for cle in legumes:
#... print(cle)
#...
#haricot
#carotte
#>>> legumes.keys()
#['haricot', 'carotte']
#>>> legumes.values()
#[48, 26]
#>>> for nom, qtt in legumes.items():
#... print("{0} ({1})".format(nom, qtt))
#...
#haricot (48)
#carotte (26)
"""
def __init__(self, **dico):
"""
On doit pouvoir creer le dictionnaire de plusieurs façons :
- Vide: sans passer aucun parametre
- Copie d'un dict(): parametre du constructeur un dict() que
l'on copie par la suite dans notre objet. On peut ainsi
ecrire constructeur(dictionnaire) et les cles et valeurs
contenues dans le dictionnaire sont copiees dans l'objet
construit.
- Pre-rempli de cles/valeurs en parametre: comme les dict()
usuels, on doit ici avoir la possibilite de pre-remplir
notre objet avec des couples cles-valeurs passes en
param (constructeur(cle1 = valeur1, cle2 = valeur2, ))
Les cles et valeurs doivent etre couplees
"""
self.kl = list()
self.vl = list()
if len(dico) != 0:
for k, v in dico.items():
self.kl.append(k)
self.vl.append(v)
def __repr__(self):
"""
Affiche l'objet dans l'interpreteur ou grâce a la fonction
print. L'affichage identique aux dict()
({cle1: valeur1, cle2: valeur2, }).
"""
content = list()
if len(self.kl) != 0:
for i in range(0, len(self.kl)):
content.append("'{}': {}".format(self.kl[i], self.vl[i]))
return "{0}{1}{2}".format(
"{",
", ".join(content),
"}"
)
def __setitem__(self, cle, valeur):
"""
Acces avec crochets pour modif (objet[cle] = valeur)
Si la cle existe on ecrase l'ancienne valeur, si elle
n'existe pas on ajoute le couple cle-valeur a la fin
"""
try:
index = self.kl.index(cle)
self.kl[index] = cle
self.vl[index] = valeur
except ValueError:
self.kl.append(cle)
self.vl.append(valeur)
def sort(self, reverse=False):
"""
L'objet doit definir les methodes sort pour le trier et reverse
pour l'inverser. Le tri de l'objet doit se faire en fonction
des cles
"""
# pour trier on stocke les couples de cle & valeur sous forme
# de tuple dans une liste temporaire
liste_temporaire = list()
if len(self.kl) != 0: # Seulement si il y a des donnees
for i in range(0, len(self.kl)): # on parcour chaque entee
liste_temporaire.append((self.kl[i], self.vl[i]))
# Tri des tuples par la valeur par une comprension de liste
liste_permute = [(val, cle) for cle, val in liste_temporaire]
liste_triee = [(cle, val) for val, cle in sorted(liste_permute, reverse=reverse)]
# On range les donnees tries dans attributs de l'objet
self.kl = [cle for cle, val in liste_triee]
self.vl = [val for cle, val in liste_triee]
def __len__(self):
""" Retourne la taille de l'objet grace a la fonction len """
return len(self.kl)
#def __contains__():
#""" Cherche une cle dans notre objet (cle in dictionnaire) """
#def __delattr__(self):
#"""
#Les cles et valeurs doivent etre couplees. Si on cherche
#a supprimer une cle sa valeur doit etre supprimee.
#"""
#def __getitem__():
#""" Acces avec crochets pour recuperer une valeur (objet[cle]) """
#def __delitem__():
#""" Acces avec crochets pour suppression (del objet[cle]) """
#def __setattr__():
#""" Function doc """
#def generateur():
#"""
#L'objet doit pouvoir etre parcouru.
#Quand on ecrit for cle in dictionnaire, on doit parcourir
#la liste des cles contenues dans le dictionnaire. A l'instar
#des dictionnaires, trois methodes keys() (renvoyant la liste
#des cles), values() (renvoyant la liste des valeurs) et
#items() (renvoyant les couples (cle, valeur)) doivent etre
#mises en œuvre. Le type de retour de ces methodes est laisse
#a votre initiative : il peut s'agir d'iterateurs ou de
#generateurs (tant qu'on peut les parcourir).
#"""
if __name__ == "__main__":
""" Active les doctests """
import doctest
doctest.testmod()

View File

@ -0,0 +1,257 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Author: freezed <freezed@users.noreply.github.com> 2018-01-25
Version: 0.1
Licence: `GNU GPL v3`: http://www.gnu.org/licenses/
"""
class DictionnaireOrdonne:
"""
Dictionnaire ordonne
====================
Notre dictionnaire ordonné. L'ordre des données est maintenu
et il peut donc, contrairement aux dictionnaires usuels, être trié
ou voir l'ordre de ses données inversées
:Example:
>>> fruits = DictionnaireOrdonne()
>>> fruits
{}
>>> fruits["pomme"] = 52
>>> fruits["poire"] = 34
>>> fruits["prune"] = 128
>>> fruits["melon"] = 15
>>> fruits
{'pomme': 52, 'poire': 34, 'prune': 128, 'melon': 15}
>>> fruits.sort()
>>> print(fruits)
{'melon': 15, 'poire': 34, 'pomme': 52, 'prune': 128}
>>> legumes = DictionnaireOrdonne(carotte = 26, haricot = 48)
# Test possible seulement avec python 3.6,
# voir: www.python.org/dev/peps/pep-0468/
#>>> print(legumes)
#{'carotte': 26, 'haricot': 48}
>>> len(legumes)
2
>>> legumes.reverse()
>>> fruits = fruits + legumes
>>> fruits
{'melon': 15, 'poire': 34, 'pomme': 52, 'prune': 128, 'haricot': 48, 'carotte': 26}
>>> del fruits['haricot']
>>> 'haricot' in fruits
False
>>> 'pomme' in fruits
True
>>> legumes['haricot']
48
>>> for cle in legumes:
... print(cle)
...
haricot
carotte
>>> fruits.keys()
['melon', 'poire', 'pomme', 'prune', 'carotte']
>>> legumes.keys()
['haricot', 'carotte']
>>> fruits.values()
[15, 34, 52, 128, 26]
>>> legumes.values()
[48, 26]
>>> for nom, qtt in legumes.items():
... print("{0} ({1})".format(nom, qtt))
...
haricot (48)
carotte (26)
>>> mots = {'olive': 51, 'identite': 43, 'mercredi': 25, 'prout': 218, 'assiette': 8, 'truc': 26}
>>> mots_ordonne = DictionnaireOrdonne(mots)
>>> mots_ordonne.sort()
>>> mots_ordonne
{'assiette': 8, 'identite': 43, 'mercredi': 25, 'olive': 51, 'prout': 218, 'truc': 26}
"""
def __init__(self, base={}, **donnees):
"""Constructeur de notre objet. Il peut ne prendre aucun paramètre
(dans ce cas, le dictionnaire sera vide) ou construire un
dictionnaire remplis grâce :
- au dictionnaire 'base' passé en premier paramètre ;
- aux valeurs que l'on retrouve dans 'donnees'."""
self._cles = [] # Liste contenant nos clés
self._valeurs = [] # Liste contenant les valeurs correspondant à nos clés
# On vérifie que 'base' est un dictionnaire exploitable
if type(base) not in (dict, DictionnaireOrdonne):
raise TypeError( \
"le type attendu est un dictionnaire (usuel ou ordonne)")
# On récupère les données de 'base'
for cle in base:
self[cle] = base[cle]
# On récupère les données de 'donnees'
for cle in donnees:
self[cle] = donnees[cle]
def __repr__(self):
"""Représentation de notre objet. C'est cette chaîne qui sera affichée
quand on saisit directement le dictionnaire dans l'interpréteur, ou en
utilisant la fonction 'repr'"""
chaine = "{"
premier_passage = True
for cle, valeur in self.items():
if not premier_passage:
chaine += ", " # On ajoute la virgule comme séparateur
else:
premier_passage = False
chaine += repr(cle) + ": " + repr(valeur)
chaine += "}"
return chaine
def __str__(self):
"""Fonction appelée quand on souhaite afficher le dictionnaire grâce
à la fonction 'print' ou le convertir en chaîne grâce au constructeur
'str'. On redirige sur __repr__"""
return repr(self)
def __len__(self):
"""Renvoie la taille du dictionnaire"""
return len(self._cles)
def __contains__(self, cle):
"""Renvoie True si la clé est dans la liste des clés, False sinon"""
return cle in self._cles
def __getitem__(self, cle):
"""Renvoie la valeur correspondant à la clé si elle existe, lève
une exception KeyError sinon"""
if cle not in self._cles:
raise KeyError( \
"La clé {0} ne se trouve pas dans le dictionnaire".format( \
cle))
else:
indice = self._cles.index(cle)
return self._valeurs[indice]
def __setitem__(self, cle, valeur):
"""Méthode spéciale appelée quand on cherche à modifier une clé
présente dans le dictionnaire. Si la clé n'est pas présente, on l'ajoute
à la fin du dictionnaire"""
if cle in self._cles:
indice = self._cles.index(cle)
self._valeurs[indice] = valeur
else:
self._cles.append(cle)
self._valeurs.append(valeur)
def __delitem__(self, cle):
"""Méthode appelée quand on souhaite supprimer une clé"""
if cle not in self._cles:
raise KeyError( \
"La clé {0} ne se trouve pas dans le dictionnaire".format( \
cle))
else:
indice = self._cles.index(cle)
del self._cles[indice]
del self._valeurs[indice]
def __iter__(self):
"""Méthode de parcours de l'objet. On renvoie l'itérateur des clés"""
return iter(self._cles)
def __add__(self, autre_objet):
"""On renvoie un nouveau dictionnaire contenant les deux
dictionnaires mis bout à bout (d'abord self puis autre_objet)"""
if type(autre_objet) is not type(self):
raise TypeError( \
"Impossible de concaténer {0} et {1}".format( \
type(self), type(autre_objet)))
else:
nouveau = DictionnaireOrdonne()
# On commence par copier self dans le dictionnaire
for cle, valeur in self.items():
nouveau[cle] = valeur
# On copie ensuite autre_objet
for cle, valeur in autre_objet.items():
nouveau[cle] = valeur
return nouveau
def items(self):
"""Renvoie un générateur contenant les couples (cle, valeur)"""
for i, cle in enumerate(self._cles):
valeur = self._valeurs[i]
yield (cle, valeur)
def keys(self):
"""Cette méthode renvoie la liste des clés"""
return list(self._cles)
def values(self):
"""Cette méthode renvoie la liste des valeurs"""
return list(self._valeurs)
def reverse(self):
"""Inversion du dictionnaire"""
# On crée deux listes vides qui contiendront le nouvel ordre des clés
# et valeurs
cles = []
valeurs = []
for cle, valeur in self.items():
# On ajoute les clés et valeurs au début de la liste
cles.insert(0, cle)
valeurs.insert(0, valeur)
# On met ensuite à jour nos listes
self._cles = cles
self._valeurs = valeurs
def sort(self):
"""Méthode permettant de trier le dictionnaire en fonction de ses clés"""
# On trie les clés
cles_triees = sorted(self._cles)
# On crée une liste de valeurs, encore vide
valeurs = []
# On parcourt ensuite la liste des clés triées
for cle in cles_triees:
valeur = self[cle]
valeurs.append(valeur)
# Enfin, on met à jour notre liste de clés et de valeurs
self._cles = cles_triees
self._valeurs = valeurs
if __name__ == "__main__":
""" Active les doctests """
import doctest
doctest.testmod()

281
package/dico_ord.py Normal file
View File

@ -0,0 +1,281 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Author: freezed <freezed@users.noreply.github.com> 2018-01-25
Version: 0.1
Licence: `GNU GPL v3`: http://www.gnu.org/licenses/
"""
class DictionnaireOrdonne:
"""
Dictionnaire ordonne
====================
Objet ressemblant a un dict, avec des capacitees de tri.
Les cles et valeurs se trouvant dans des listes de meme
taille, il suffira de prendre l'indice dans une liste pour
savoir quel objet lui correspond dans l'autre. Par exemple,
la cle d'indice 0 est couplee avec la valeur d'indice 0.
:Example:
>>> fruits = DictionnaireOrdonne()
>>> fruits
{}
>>> fruits["pomme"] = 52
>>> fruits["poire"] = 34
>>> fruits["prune"] = 128
>>> fruits["melon"] = 15
>>> fruits
{'pomme': 52, 'poire': 34, 'prune': 128, 'melon': 15}
>>> fruits.sort()
>>> print(fruits)
{'melon': 15, 'poire': 34, 'pomme': 52, 'prune': 128}
>>> legumes = DictionnaireOrdonne(carotte = 26, haricot = 48)
# Test possible seulement avec python 3.6,
# voir: www.python.org/dev/peps/pep-0468/
#>>> print(legumes)
#{'carotte': 26, 'haricot': 48}
>>> len(legumes)
2
>>> legumes.reverse()
>>> fruits = fruits + legumes
>>> fruits
{'melon': 15, 'poire': 34, 'pomme': 52, 'prune': 128, 'haricot': 48, 'carotte': 26}
>>> del fruits['haricot']
>>> del fruits['betterave']
ValueError: «'betterave' is not in list»
>>> 'haricot' in fruits
False
>>> 'pomme' in fruits
True
>>> legumes['haricot']
48
>>> fruits['betterave']
ValueError: «'betterave' is not in list»
>>> for cle in legumes:
... print(cle)
...
haricot
carotte
>>> fruits.keys()
['melon', 'poire', 'pomme', 'prune', 'carotte']
>>> legumes.keys()
['haricot', 'carotte']
>>> fruits.values()
[15, 34, 52, 128, 26]
>>> legumes.values()
[48, 26]
>>> for nom, qtt in legumes.items():
... print("{0} ({1})".format(nom, qtt))
...
haricot (48)
carotte (26)
>>> liste = [0,1,2,3]
>>> tentative1 = DictionnaireOrdonne(liste)
Un dict() est attendu en argument!
>>> dico_vide = {}
>>> tentative2 = DictionnaireOrdonne(dico_vide)
>>> tentative2
{}
>>> mots = {'olive': 51, 'identite': 43, 'mercredi': 25, 'prout': 218, 'assiette': 8, 'truc': 26}
>>> mots_ordonne = DictionnaireOrdonne(mots)
>>> mots_ordonne.sort()
>>> mots_ordonne
{'assiette': 8, 'mercredi': 25, 'truc': 26, 'identite': 43, 'olive': 51, 'prout': 218}
"""
def __init__(self, filled_dict={}, **kwargs):
"""
Peu prendre aucun parametre ou:
- un dictionnaire 'filled_dict' en 1er argument
- des valeurs nommees dans 'kwargs'
"""
# Creation des attributs qui stokeront les cles et valeurs
self._keys_list = list()
self._values_list = list()
# Si 'filled_dict' est un dict() non vide, ajout du contenu
if type(filled_dict) not in (dict, DictionnaireOrdonne):
#raise TypeError("Un dict() est attendu en argument!")
print("Un dict() est attendu en argument!")
else:
for key, val in filled_dict.items():
self._keys_list.append(key)
self._values_list.append(val)
# Si les kwargs ne sont pas vide, ajout du contenu
if len(kwargs) != 0:
for key, val in kwargs.items():
self._keys_list.append(key)
self._values_list.append(val)
def __add__(self, other_dict_ord):
"""
On doit pouvoir ajouter deux dictionnaires ordonnes
(dico1 + dico2) ; les cles et valeurs du second dictionnaire
sont ajoutees au premier.
"""
i = 0
while i < len(other_dict_ord):
self._keys_list.append(other_dict_ord._keys_list[i])
self._values_list.append(other_dict_ord._values_list[i])
i += 1
return self
def __contains__(self, key_to_find):
""" Cherche une cle dans notre objet (cle in dictionnaire) """
return key_to_find in self._keys_list
def __delitem__(self, key_to_del):
""" Acces avec crochets pour suppression (del objet[cle]) """
try:
index_to_del = self._keys_list.index(key_to_del)
except ValueError as except_detail:
print("ValueError: «{}»".format(except_detail))
else:
del self._keys_list[index_to_del]
del self._values_list[index_to_del]
def __iter__(self):
"""Parcours de l'objet, renvoi l'iterateur des cles"""
return iter(self._keys_list)
def __getitem__(self, key_to_get):
""" Acces aux crochets pour recuperer une valeur (objet[cle]) """
try:
find_key = self._keys_list.index(key_to_get)
except ValueError as except_detail:
print("ValueError: «{}»".format(except_detail))
else:
print(self._values_list[find_key])
def __len__(self):
""" Retourne la taille de l'objet grace a la fonction len """
return len(self._keys_list)
def __repr__(self):
"""
Affiche l'objet dans l'interpreteur ou grâce a la fonction
print: ({cle1: valeur1, cle2: valeur2, }).
"""
# contiendra le txt a afficher
object_repr = list()
# Si l'objet n'est pas vide
if len(self._keys_list) != 0:
for i in range(0, len(self._keys_list)):
object_repr.append("'{}': {}".format(self._keys_list[i], self._values_list[i]))
return "{0}{1}{2}".format(
"{",
", ".join(object_repr),
"}"
)
def __setitem__(self, cle, valeur):
"""
Acces avec crochets pour modif (objet[cle] = valeur). Si la cle
existe on ecrase l'ancienne valeur, sinon on ajoute le couple
cle-valeur a la fin
"""
try:
index = self._keys_list.index(cle)
self._keys_list[index] = cle
self._values_list[index] = valeur
except ValueError:
self._keys_list.append(cle)
self._values_list.append(valeur)
def __str__(self):
"""
Methode pour afficher le dictionnaire avec «print()» ou pour
le convertir en chaine grâce aec «str()». Redirige sur __repr__
"""
return repr(self)
def keys(self):
"""
La methode keys() (renvoyant la liste des cles) doit etre
mises en œuvre. Le type de retour de ces methodes est laisse
a votre initiative : il peut s'agir d'iterateurs ou de
generateurs (tant qu'on peut les parcourir)
"""
return list(self._keys_list)
def sort(self, reverse=False):
"""
L'objet doit definir les methodes sort pour le trier et reverse
pour l'inverser. Le tri de l'objet doit se faire en fonction
des cles
"""
# Peut etre un peu overkill… voir methode dans la correction
# pour trier on stocke les couples de cle & valeur sous forme
# de tuple dans une liste temporaire
liste_temporaire = list()
if len(self._keys_list) != 0: # Seulement si il y a des donnees
for i in range(0, len(self._keys_list)): # on parcour chaque entee
liste_temporaire.append((self._keys_list[i], self._values_list[i]))
# Tri des tuples par la valeur par une comprension de liste
liste_permute = [(val, cle) for cle, val in liste_temporaire]
liste_triee = [(cle, val) for val, cle in sorted(liste_permute, reverse=reverse)]
# On range les donnees tries dans attributs de l'objet
self._keys_list = [cle for cle, val in liste_triee]
self._values_list = [val for cle, val in liste_triee]
def reverse(self):
"""
L'objet doit definir les methodes sort pour le trier et reverse
pour l'inverser. Le tri de l'objet doit se faire en fonction
des cles
"""
return self.sort(reverse=True)
def items(self):
"""Renvoi un generateur contenant les couples (cle, valeur)"""
for key, val in enumerate(self._keys_list):
yield (val, self._values_list[key])
def values(self):
"""
La methode values() (renvoi la liste des valeurs) doit etre
mises en œuvre. Le type de retour de ces methodes est laisse
a votre initiative : il peut s'agir d'iterateurs ou de
generateurs (tant qu'on peut les parcourir)
"""
return list(self._values_list)
if __name__ == "__main__":
""" Active les doctests """
import doctest
doctest.testmod()