Dictionnaires par clés et valeurs

Représentation des données: types construits

Plan

Introduction

Tout comme les listes, les dictionnaires sont des séquences. Contrairement aux listes les dictionnaires ne sont pas ordonnés. Un dictionnaire est constitué de paires de la forme clé: valeur. Les clés doivent être uniques et immuables: chaine de caratères, tuple contenant des immuables, nombres. On retrouvera une valeur gràce à sa clé. En informatique, les données strucuturées comme les dictionnaires python, s'appellent des tables de hachages.

Concrêtement

On crée des dictionnaires avec des accolades { }. Imaginons une liste de produits et leur prix:
produit={ 'ordinateur' : 1000 , 'imprimante': 250 , 'bureau':300 }
print(produit['imprimante'])
#affiche 250
del produit['ordinateur']
produit['chaise']= 180
print(produit)
#affiche {'imprimante': 250, 'bureau': 300, 'chaise': 180}
print('imprimante' in produit)
# affiche True
On peut construire un dictionnaire à partir d'une liste de paires de tuples avec le constructeur dict(), ou par compréhension, ou avec des paramètres nommés. Bref, l'utilisation des dictionnaires est souple et l'association d'une clé est dans bien des cas plus parlante qu'un index. Voici les trois constructions décrites ci-dessus:
dico=dict( [('ordinateur',1000), ('souris',50)])
print(dico)
#affiche {'ordinateur':1000, 'souris':50}
dico2={x: x**2 for x in (2, 4, 6)}
print(dico2)
#affiche {2: 4, 4: 16, 6: 36}
dico3=dict('ordinateur'=1000, 'souris'=50)
#ne fonctionne que si la clé est une chaine de caractères
print(dico3)
# affiche {'ordinateur': 1000, 'souris': 50}
print(dico3["ordinateur"])
# affiche 1000
  On peut aussi créer un dictionnaire avec l'instruction zip. Cette commande permet de regrouper une liste pour les clés qui doivent donc toutes être différentes et une liste pour les valeurs.
prenom=['johan','nathan','didier']
infos=[['1978-02-20',175],['1990-10-02',180],['1966-12-30',161]]
monDico= dict(zip(prenom,infos))
print(monDico)
#affiche {'johan': ['1978-02-20', 175], 'nathan': ['1990-10-02', 180], 'didier': ['1966-12-30', 161]}

Pour ajouter un élément il suffit de taper, pour un dico existant, dico[nouvelle clé]=valeur associée! Pour effacer on utilise del dico[clé]. La fonction pop supprime ET renvoie la valeur associée à la clé.

monDico["Laura"]=['1978-02-20',175]
del monDico["nathan"]
print(monDico)
#pour vérifier si une clé existe:
booleen="didier" in monDico
print("didier fait parti du dico ?",booleen)
 

Boucles sur dictionnaires

Pour afficher les clés et leur valeur, on utilise la méthode iteritems(). Pour afficher que les clés on utilise la méthode keys, et pour afficher uniquement les valeurs, la méthode values
produit={ 'ordinateur' : 1000 , 'imprimante': 250 , 'bureau':300 }
for prod, prix in produit.items():  #ou iteritems selon versions python
    print( prod, prix)
for prod in produit.keys():
	print(prod)
for prix in produit.values():
	print(prix)

Un exemple: les données EXIF

Lorsqu'un appareil photo numérique sauvegarde une photo, il enregistre l'image mais aussi tout un tas d'autres informations: marque de l'appareil, date,...et même les coordonnées gps si l'appareil sait se situer! Ces données sont nommées EXIF(Exchangeable Image File).

Dans Windows, un clic droit sur l'image et l'onglet détails donne les informations exif.

En python, la bibliothèque PIL contient les modules Image et ExifTags qui permettent d'acceder rapidement aux tags. Voici le code Python3 ci-dessous. Pour que cela fonctionne il faut installer PIL en le téléchargeant.

Pour installer des bibliothèques non présententes dans Python, vous devez ouvrir lignes de commandes (pour windows) puis taper: pip install leNomDeLabBibliothèque. Il arrive que certaines bibliothèques nécessitent d'être téléchargées avant de faire le pip install

Comme nous n'avons pas accès à ligne de commandes en classe, nous devons abandonner idle. Nous allons utiliser Jupyter qui permet de faire du Python dans un navigateur et lorsqu'il est installé sur les postes, on peut utiliser et installer des bibliothèques directement dans l'éditeur.

Quelques remarques sur l'utilisation de Jupyter. Vous devez passer par anaconda pour accéder à Jupyter:

  • Lancer Jupyter
  • S'il ne s'ouvre pas dans firefox (ce qui est le cas pour l'instant chez nous), copiez le lien proposé dans une fenêtre firefox. En haut à droite, cliquez sur new et choisissez Python3. Vous êtes alors dans une page notebooks Jupyter.
  • Vous pouvez aussi chez vous utiliser Jupyter directement en ligne sans installation préalable mais certaines bibliothèques (webbrowser par exemple) ne fonctionneront pas correctement.

Contrairement à l'idle, vous ne sauvegardez pas un programme mais une page qui contient des entrées. Chaque entrée est un bout de code, ou un programme. Vous pouvez modifier et relancer une entrée plutôt que de retaper tout le code. Vous avez toujours sous les yeux les rendus de vos programmes.

Téléchargez la photo. puis cliquer dans jupyter home et upload pour la mettre dans votre espace de travail de jupyter. Par défaut vous retrouvez vos fichiers dans votre pc dans votre compte utilisateur.

from PIL import Image, ExifTags

#premier code d'extraction de exif
def get_exif1(image):
    ret = {}
    img = Image.open(image)
    info = img._getexif()
    for tag, value in info.items():
        decoded = ExifTags.TAGS.get(tag, tag)
        if tag in ExifTags.TAGS:
            ret[decoded] = value
    return ret

#deuxième code d'extraction de exif
#par compréhension
def get_exif2(image):
    img = Image.open(image)
    exif_data = img._getexif()
    exif = {
        ExifTags.TAGS[k]: v
        for k, v in img._getexif().items()
        if k in ExifTags.TAGS
    }
    return(exif)

#affichage des données gps
def gpsInfo(exifData):
    gpsinfo = {}
    for key in exifData['GPSInfo'].keys():
        decode = ExifTags.GPSTAGS.get(key,key)
        gpsinfo[decode] = exifData['GPSInfo'][key]
    return gpsinfo

#conversion degrés,minutes,secondes en DD(degrés décimaux)
def convertGpsToDd(deg,minute,sec):
    return(deg+minute/60+sec/3600)

#tests
data1 = get_exif1('img2.jpg')
print(data1)

data2=  get_exif2('img2.jpg')
print(data2)
print("nom de l'appareil:",data2['Make'])

print("infos gps: ",data2['GPSInfo'])
gps=gpsInfo(data2)
for i,j in gps.items():
    print(i,j)

La ligne print("infos gps: ",data2['GPSInfo']) donne pour ma photo l'affichage suivant (sinon ma fonction gpsinfo donne les infos plus clairement):

infos gps:  {1: 'N', 2: ((43, 1), (36, 1), (10791, 1000)), 3: 'E',
 	 4: ((3, 1), (52, 1), (19506, 1000)), 5: 0, 6: (44000, 1000),
 	 7: ((8, 1), (29, 1), (26, 1)), 29: '2016:05:22'}
 

Un dicitionnaire qui contient ici 8 clés

Photo prise dans l'hémisphère Nord, la lattitude est (43, 1), (36, 1), (10791, 1000) soit 43 degrés, 36 minutes, 10.791 secondes à l'Est du méridien de Greenwich. La longitude est (3, 1), (52, 1), (19506, 1000) donc 3°52'19.506' et une date et même l'heure 8h29'26 et l'altitude de 44m! Utilisez un site qui permet de se localiser. Où a été prise cette photo?

Voici le code pour afficher la carte et l'endroit où la photo a été prise. Il faudra au préalable installer la bibliothèque folium! On peut faire cela directement dans Jupyter en tapant

pip install folium

import folium
import webbrowser

x=43+36/60+10.791/3600
y=3+52/60+19.506/3600
print(x,y)

carte= folium.Map(
    location=[x, y],
    zoom_start=17
)

folium.Marker(
    location=[x, y],
    popup='Où est-on?',
    icon=folium.Icon(color='red')
).add_to(carte)


folium.CircleMarker(
    location=[x, y],
    radius=400,
    popup='Le quartier',
    color='#ff0000',
    fill=True,
    fill_color='#ccccccc'
).add_to(carte)


carte.save('exempleFolium1.html')
webbrowser.open('exempleFolium1.html',new=2)

Voici comment modifier les données exifs. Vous pourrez donc faire croire qu'une photo qui n'est pas de vous a été prise avec votre téléphone à la date de votre choix, ou qu'une image d'un lieu de la région est à un autre endroit! Allez sur la doc de piexif pour plus de détails.

Il faut aussi installer la bibliothèque piexif :

pip install piexif

from PIL import Image
import piexif

im = Image.open('img2.jpg')
exif_dict = piexif.load(im.info["exif"])

print(exif_dict["GPS"])
w, h = im.size

exif_dict["0th"][piexif.ImageIFD.YResolution] = (h, 1)
exif_dict["GPS"][piexif.GPSIFD.GPSVersionID]= (2,0,0,0)
exif_dict["GPS"][piexif.GPSIFD.GPSLatitudeRef] = "S"
exif_dict["GPS"][piexif.GPSIFD.GPSAltitude] = (20,1)
exif_bytes = piexif.dump(exif_dict)
im.save('new_file.jpg', "jpeg", exif=exif_bytes)

Afin de pouvoir réaliser l'exercice 2 je vous donne le code suivant qui permet de lire dans un dossier tous les fichiers qui ont pour extension jpg. Pour cela on utilise la bibliothèque operating system de Python os

	#1)recuperation du contenu du dossier c:
	contenu=os.listdir('c:')
	#donne le chemin complet de chaques fichiers du dossier, par exemple pour le fichier img2.jpg : 'c:/001.jpg'
	#2)recuperation des noms de fichiers (sans le chemin) qui sont des .jpg:
	#méthode par compréhension de liste.
	# Split de la chaine de caractère sur le symbole / -> cela donne une liste
	# avec le [-1] on ne garde que le dernier de la liste...donc le nom du fichier
	# mais uniquement si ce nom contient .jpg
	contenu=[x.split('/')[-1] for x in contenu if '.jpg' in x.split('/')[-1]]
	#3)Maintenant on trie la liste

Exercices

  1. Créer un dictionnaire avec pour clé des prénoms et pour valeur une liste contenant les noms des spécialités de ces élèves. L'initialiser avec 2 élèves. Taper le code pour ajouter un élève. Taper le code pour supprimer le premier élève que vous avez saisi. Modifier ensuite une des trois spécialité et la remplacer par 'abandonnée'. Faites ensuite afficher tout le contenu de ce dictionnaire.

  2. Créer un dictionnaire qui contient les 10 (de 0 à 9) premiers nombres entiers naturels en nombre pour la clé et en français pour la valeur. Faites en sorte qu'une phrase du type "j'ai 3 pommes" se transforme toute seule en 'j'ai trois pommes'. Penser à la leçon sur chr, ord et le codage ASCII (UTF8 dans les faits). Pour être clair, vous direz: "entrez une phrase avec des nombres entiers sous forme de nombres" et votre programme remplacera les nombres par des chaines de caractères. (on s'arrête à 9, mais on peut réfléchir à plus...)

  3. Réunir les programmes de la partie exif ci-dessus pour passer directement d'un fichier jpg à sa localisation.

  4. Réunir les programmes de la partie exif ci-dessus pour afficher toutes les images d'un dossier sur une même carte.

  5. Prenez une photo dans votre téléphone (ou sur internet) et faites moi croire que la photo date du week-end dernier dans un lieu lointain que vous aurez préalablement choisi. Afficher le résultat avec le travail des exercices précédents.

Android

De la programmation pour pc à la programmation pour téléphone.

A finir

Pas eu le temps de tout faire.....