Bien ! Nous avons vu, dans les bases de Python, ce que sont les listes, des types de données qui contiennent plusieurs valeurs numérotées. Nous avons aussi dit qu'il faisait parti de la famille des conteneurs, un grande famille de types qui peuvent en stocker plusieurs.
En effet, les listes sont bien utiles et très fréquentes, mais il va falloir des fois utiliser d'autres moyens de stocker les données. Ceci était d'autant plus vrai avant, à l'époque où les performances d'une machines étaient bien plus limitées ! Nous allons donc faire un tour des autres conteneurs du langage Python.
Tout d'abord, révélation: les string sont des conteneurs ! En effet, un texte est en fait une longue liste de caractères à la suite, et il faut donc bien les mettre dans un conteneur ! Les string sont donc des conteneurs qui ont été faits pour optimiser leur utilisation. La plupart des langages les utilisent, par exemple le C++, où, pour les utiliser, il faut importer le module <string> !
Comme c'est un conteneur, on peut avoir accès à chacun des caractères, séparément. Pour cela, on utilise la même méthode que pour les listes, soit en mettant entre crochets son index ! Voici un exemple en mode interactif:
>>> texte = "Bonjour les gens !"
>>> texte[0]
'B'
>>> texte[5]
'u'
>>> texte[-1]
'!'
Cependant, notez bien une particularité des string: on ne peut pas modifier chacun des caractères ! On dit que les string sont immuables. Voici un exemple qui ne fonctionne pas:
>>> texte[0] = 'O'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
La raison de ceci est un peu complexe (et liée à un autre conteneur que nous allons voir), mais on peut comprendre que lorsque nous avons une grande chaîne de caractère, nous n'avons pas à la base besoin de modifier un à un chaque caractère. Notez donc que l'on peut lire un caractère, mais pas le modifier.
Pour mettre dans la mémoire une chaîne de caractère, chaque caractère est associé à un nombre selon une convention, l'encodage. Il en existe plusieurs, dont le très connu ASCII, plutôt léger, mais qui ne permet pas d'utiliser les accents, quasiment absent en anglais, ainsi que certains caractères comme € ou ß. L'UTF-8 permet, lui, de les utiliser et Python utilise cet encodage par défaut.
Vous pouvez jouer avec l'encodage en utilisant les fonctions chr et ord.
>>> ord("A")
65
>>> chr(65)
'A'
string peut aussi être utilisé dans une boucle itérative, comme ceci:
>>> texte = "Hello !"
>>> for lettre in texte:
... print(lettre)
...
H
e
l
l
o
!
Il existe aussi plein de fonctionnalité avec les string, qui peuvent être notamment utiles pour lire ou écrire du texte avec la console. En voici une partie:
>>> "Hello" + " world!" #Met côte à côte les string
'Hello world!'
>>>
>>> "{} et {} font {} !".format(1, 2, 3) #Remplace les accolades par les valeurs
'1 et 2 font 3 !'
>>>
>>> " | ".join(["ab", "cd", "ef"])
'ab | cd | ef'
>>>
>>> "Salut tout le monde !".split() #Coupe le string à chaque espace et renvoie une liste des coupures
['Salut', 'tout', 'le', 'monde', '!']
>>> "Salut tout le monde !".split("t") #On peut changer les points de coupe
['Salu', ' ', 'ou', ' le monde !']
Nous venons de voir que les chaines de caractères sont immuables. Il existe aussi un type, semblable aux listes, qui est cependant immuable: les tuples.
Un tuple est, tout simplement, une liste qui ne peut pas être modifiée, c'est à dire que vous ne pouvez pas le modifier. Pour l'écrire, il suffit d'utiliser des virgules entre les valeurs:
>>> mon_tuple = 1, 2, 3
>>> mon_tuple
(1, 2, 3)
On utilise généralement des parenthèses au début et à la fin, même si elles sont facultatives. On peut faire un tuple avec une seule valeur, mais il faut alors mettre une virgule. Pour définir un tuple vide (ce qui sert à quasiment rien), on utilise des parenthèses vides.
Bon, je vous l'accorde, ça n'envoie pas du lourd... Mais les tuples peuvent être utilisés dans le fonctionnement de Python ! C'est avec des tuples que nous pouvons donner des valeurs à plusieurs variables en même temps ! Regardez bien:
a, b = 1, 2
Eh oui, il y a des virgules, donc les deux cotés de l'affectation sont des tuples ! L'interpréteur va ici prendre les deux tuples, prendre les valeurs du deuxième et les assigner aux variables du premier.
Les tuples peuvent aussi être utilisés pour faire renvoyer à une fonction plusieurs sorties ! Voici un petit exemple:
>>> def donne_ton_nom():
... return "Olivier", 14
...
>>> donne_ton_nom()
('Olivier', 14)
>>> nom, age = donne_ton_nom()
>>> print(nom, age)
'Olivier' 14
Ici, notre fonction doit renvoyer 'Olivier' et 14. Pour cela, il va mettre ces deux valeurs dans un tuple. On peut ensuite, par exemple, utiliser le tuple renvoyer pour donner aux variables les valeurs renvoyés par la fonction.
Ah ! Nous allons maintenant voir un conteneur plutôt nouveau, les dictionnaires. Pour l'instant, nous avons vu les listes, tuples et string. Leur éléments sont numérotés avec un index à partir de 0 et on a besoin de ce numéro pour accéder à un des éléments. Les dictionnaires sont différents, car leurs éléments ne sont pas accessibles grâce à un index, mais grâce à une clé que l'on spécifie, qui peut être un nombre, mais aussi un float, un tuple ou encore un string !
Voyez ceci comme... ben des dictionnaires, le mot vient de là ! Un dictionnaire associe un mot à une définition, on peut donc considérer le mot comme une clé et la définition comme l'élément. Mais cela ne plait pas qu'à Larousse, car nous pouvons avec ceci faire, par exemple, un carnet d'adresse, un registre du stock de chaque produit d'une épicerie, une carte où des coordonnées (sous forme de tuple) sont des clés pour avoir accès à ce qu'il y a dessus... C'est aussi un type de donnée assez rapide à utiliser, donc la plupart des langages l'utilisent, souvent avec le nom de map en anglais. Certains les appellent aussi des tables de hashage à cause de leur mécanisme interne, même si c'est quand même moins explicite !
Voici comment créer un dictionnaire en Python:
{clé_1: élément_1, clé_2: élément_2, ...} #Entre accolades
dict(clé_1=élément_1, clé_2=élément2, ...) #Dans un constructeur
Remarquez la différence avec les listes: nous devons ici écrire chaque élément et sa clé et la position de l'élément dans l'écriture n'est pas important.
Le dictionnaire créé, nous pouvons accéder à un élément en écrivant sa clé entre crochets. Nous pouvons alors le lire, mais aussi le modifier. Voici un exemple:
>>> mon_dict = {"Tomate": "Fruit très bon",
... "Canard": "Animal qui vole et fait coin-coin",
... "Stylo": "Outil permettant d'écrire"} #Une écriture lisible et autorisée
>>> mon_dict["Stylo"]
"Outil permettant d'écrire"
>>> mon_dict["Tomate"] = "Légume et fruit très bon"
>>> mon_dict["Tomate"]
'Légume et fruit très bon'
>>> mon_dict["Euro"] = "Monnaie utilisée au sein de l'UE"
>>> mon_dict["Euro"]
"Monnaie utilisée au sein de l'UE"
Voyez ce qui se passe lorsque j'écris mon_dict["Euro"] = "Monnaie utilisée au sein de l'UE": cela crée un nouvel élément dans le dictionnaire que nous pouvons réutiliser plus tard.
J'ai écrit ici un exemple avec des string, pour filer l'analogie du dictionnaire, mais gardez en tête que nous pouvons mettre d'autres types de données en clé et en valeur ! Il n'y a qu'une contrainte pour le type de la clé: il doit être immuable ! Vous pouvez donc utiliser des string (voilà une raison de l'immuabilité des string), les tuples, les nombres... Mais pas les listes et les dictionnaires ! En effet, on peut les modifier, et le fonctionnement interne des dictionnaires fait que l'on ne doit pas pouvoir modifier les clés d'un dictionnaire.
Il existe toute une série de fonctionnalité sympas sur les dictionnaires en Python:
Voir si une clé est dans un dictionnaire: clé in dictionnaire
Avoir un conteneur avec les clés: dictionnaire.keys()
Avoir un conteneur avec les valeurs: dictionnaire.values()
Supprimer une clé du dictionnaire: del dictionnaire[clé]
Faire une boucle itérative sur les clés d'un dictionnaire: for clé in dictionnaire:
Faire une boucle itérative sur les clés + valeurs d'un dictionnaire: for clé, valeur in dictionnaire.items():
Voici une liste d'exemples:
>>> d = {"Canard": "Coin coin", "Vache": "Meuh", "Poule": "Cot cot"}
>>> "Vache" in d
True
>>> "Champignon" in d
False
>>> d.keys()
dict_keys(['Canard', 'Vache', 'Poule'])
>>> d.values()
dict_values(['Coin coin', 'Meuh', 'Cot cot'])
>>> del d["Poule"]
>>> d
{'Canard': 'Coin coin', 'Vache': 'Meuh'}
>>> for animal, bruit in d.items():
... print(animal, ":", bruit)
...
Canard : Coin coin
Vache : Meuh
Les dictionnaires sont donc un type très utiles, avec encore une fois plus de fonctions que ce que je viens de dire, encore une fois allez voir la documentation sur les dictionnaires pour plus de détails. Les dictionnaires sont donc un type assez utile, il est d'ailleurs utiliser par l'interpréteur pour quelque chose que nous allons voir plus tard, les objets.
Python offre plusieurs conteneurs utiles, avec ceux que nous venons de voir, mais aussi les sets, les itérateurs, qui méritent qu'on en parle plus tard, mais aussi toute une liste, dans le module collections. Mais nous allons finir ce chapitre en parlant d'une possibilité des listes: comme elles peuvent contenir n'importe quelle chose en Python et qu'elles en sont, on peut très bien mettre des listes dans des listes ! Voici un exemple:
exemple = [[1, 2, 3, 4], [5, 6, 4], [7, 6, 8]]
A quoi ça sert ? Eh bien, cela permet par exemple de représenter des tableaux en 2D ! Imaginez que nous voulions faire une image (certes primitive, mais une image). Une image a deux dimensions: une longueur et une largeur. Nous pouvons alors "découper" en tranches l'image, ligne par ligne, et lister tous les éléments de ces lignes. Voici un exemple primitif:
>>> image = [["#", "#", "."],
["#", "x", "."],
["x", "x", "."]]
>>> for ligne in image:
... for pixel in ligne: #ligne est une liste
... print(pixel, end="")
... print()
##.
#x.
xx.
On peut effectuer des opérations sur ces images, les agrandir, les réduire, etc. Nous pouvons aussi les utiliser pour faire des matrices (un objet mathématique qui ressemble à un tableau à 2 dimensions), etc.
Pour accéder à un élément de la liste de liste, on donne déjà l'index de la liste, puis l'index de l'élément dans la liste, comme ceci:
>>> image[2][1] #élément d'index 1 dans la liste d'index 2
'x'
>>> image[0][1]
'#'
En fait, lorsque l'interpréteur lit image[2][1], il va faire ceci: (image[2])[1]. Il va alors chercher l'élément 2 de image, soit ["x", "x", "."], puis l'élément 1 de cette liste, soit "x" (rappelez-vous que les index commencent à 0).
Les sets :
Jusqu'à présent, nous n'avons vu que des conteneurs "rangés", c'est-à-dire que les éléments étaient placés dans l'ordre et on pouvait choisir lequel on voulait. Et bien les sets euh ne sont pas comme ça, ils sont "désordonnés": aucun ordre n'est vraiment établi. On peut le voir dans l'interpréteur Python :
>>> s = {1,2,3}
>>> s[1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'set' object is not subscriptable
>>>
Mais alors à quoi ça sert ??
Et bien c'est vrai que l'utilisation des sets n'est pas forcément évidente, mais imaginons qu'on veuille simplement stocker des données dont l'ordre n'est pas important, et bien dans cette situation l'utilisation des sets peut être pertinente :
>>> liste_amis = {"Martin","Éric","Zoé"}
>>> print(liste_amis)
{'Zoé', 'Martin', 'Eric'}
Parce que quand on ne va pas donner une ordre à nos amis...
Pour le reste les sets fonctionnent de la même manière que les autres conteneurs :
>>> for ami in liste_amis:
... print(ami + " est mon ami(e) !")
...
Zoé est mon ami(e) !
Martin est mon ami(e) !
Eric est mon ami(e) !
>>> liste_amis.add("Nathan")
>>> liste_amis
{'Zoé', 'Nathan', 'Martin', 'Eric'}
>>> liste_amis.remove("Nathan")
>>> liste_amis
{'Zoé', 'Martin', 'Eric'}
>>> len(liste_amis)
3
Une autre particularité interessante des sets est qu'il ne peut pas y avoir 2 élèments identiques :
>>> liste_amis
{'Zoé', 'Martin', 'Eric'}
>>> len(liste_amis)
3
>>> liste_amis.add("Martin")
>>> liste_amis
{'Zoé', 'Martin', 'Eric'}
>>> len(liste_amis)
3
On peut donc utiliser les sets pour éviter de stocker 2 fois la même chose (Ce serait quand même bête).