Cette page (re)pose ici quelques fondamentaux de Python utiles pour faire des graphiques, notamment la notion de variable, de conteneur (listes, tuples...) et de fonctions et leurs arguments (de position, nommés avec valeur par défaut, en nombre indéfini...). On abordera aussi d'autres notions, comme la portée des variables. Normalement, on commence par les bases et on évoque ensuite des propriétés plus complexes ; dès que ça devient trop théorique, attendez d'en avoir besoin et jouez sur la complémentarité TDs/théorie, ainsi que sur la complémentarité avec les autres sources d'info !
PYTHON TUTOR est un outil pédagogique, qui exécute du code en ligne, pas à pas, en représentant graphiquement tous les objets créés (chiffres, chaînes de caractères et conteneurs (listes, tuples...) contenant les objets élémentaires), et ceci dans leurs espaces de nommage respectifs, avec les liens avec les noms de variables. Cela vous permettra en particulier de voir comment la modification d'une variable impacte (ou pas) certaines autres. Il est limité par le nombre d'instructions, et ne permet pas l'import de modules (en particulier, pas de matplotlib...)
🐍 Bases pour débuter
A A1) découverte des instructions de base dans la console
En séance, on fera les premiers pas dans la console. Si vous préférez, ou si vous préférez garder une trace, vous pourrez créer un fichier *.py ou un notebook (attention aux petites différences).
Premier contact : variables et listes
En python, on ne déclare pas les variables ; par contre, elles ont bien un type.
Python se débrouille pour assigner le bon type (
Règles de nommage : peu de choses sont interdites en Python, mais un nom de variable doit commencer par une lettre (
Python est un langage sensible à la "casse", c'est à dire que les lettres en minuscule sont différentes des lettres en majuscules...
Donc ma_variable, MA_VARIABLE, Ma_Variable et ma_Variable sont des noms différents...
Principales conventions : rien n'est obligatoire en Python, mais il est fortement recommandé de respecter les conventions suivantes pour faciliter la lecture d'autres codes, notamment :
- noms de variables :
🐍 en snake-case, les mots sont en minuscules et reliés par des underscore qui le font ressembler à un animal articulé : ma_variable_peut_avoir_un_nom_assez_long - noms de classes :
🐫 en CapWords, autrement dit UpperCaseCamelCase : un nom qui commence par une capitale, ou plusieurs mots collés avec chacun une capitale, ce qui crée des bosses dans le nom : MaClasseQuiFaitUnTrucSympa. Les instances de classe seront écrites en snake-case : instance_de_la_classe_sympa = MaClasseQuiFaitUnTrucSympa() - noms de constantes : en snake-case majuscule ("SCREAMING_SNAKE_CASE") : COEFFICIENT, CODE_LACUNE.
⚠ ce n'est qu'une convention donc en pratique vous pouvez modifier vos constantes en cours de programme... - (il y en a d'autres, voir les [PEP8](https://www.python.org/dev/peps/pep-0008/], [éventuellement en chanson...]https://youtu.be/hgI0p1zf31k))
a = 10 # assignation, sans préciser le type, Python comprendra : "duck typing"
type(a) # réponse = int (typage réalisé par Python en fonction de l'entrée)
Python comprend quel type est affecté à un nom de variable, on parle de "duck typing" : quand vous entendez un canard vous savez que c'est un canard ... Pour travailler avec une collection d'éléments, il existe plusieurs structures en Python. On va utiliser dans un premier temps le conteneur le plus "basique", la liste.
une_liste_vide = list() # on crée une _instance_ de la classe _liste_
une_autre_liste_vide = [] # les CROCHETS suffisent à signaler à Python que vous avez créé une liste
ma_liste = [1, 2, 5, 10, 50] # une liste, définie comme telle par ses CROCHETS, avec ses éléments
ma_liste[1] # une liste est INDEXABLE* ; le résultat peut vous surprendre : Python commence l'indexation à ZERO, eh oui
12 in ma_liste # quel est le type de la réponse renvoyée ?
ma_liste[2]= 12 # je peux changer un élément de la liste ; c'est possible car une liste est MUTABLE*
ma_liste.append(100) # j'ajoute un élément à la liste ; c'est possible car une liste est MUTABLE*
ma_liste_fourre_tout = [1, 2, 5, 10, "camembert" , 50.5, (8,12), ma_liste]
Le conteneur liste peut être un fourre-tout... mais ce n'est pas recommandé ! Pour du calcul matriciel, on préférera la structure de numpy.array (objet array du module numpy). On verra d'autres conteneurs par la suite : tuples (à faire) et dictionnaires : A4.
En Python, pas de 'begin" ni de 'end' ni d'accolades pour délimiter les blocs : on définit des blocs de code par la seule indentation : le bloc est annoncé par ":" et les lignes appartenant au bloc sont indentées de 4 espaces (ou une tabulation) par rapport à la ligne qui annonce le bloc. C'est une habitude à prendre, et votre environnement de développement vous aidera beaucoup (sous PyCharm : on peut utiliser indifféremment 4 espaces ou une tabulation, et pour réajuster des blocs il y a Edit/Indent ou /Unindent).
for element in ma_liste: # une liste est ITERABLE* ; tous les conteneurs sont itérables
print(element) # indentation de 4 espaces, on est dans le bloc de code
print("fini") # l'indentation a cessé, on a quitté le bloc
A2) Pièges et astuces des boucles
ma_liste = [1, 2, 5, 10, 50] # une liste d'entiers
for element in ma_liste: # une liste est ITERABLE* ; tous les conteneurs sont itérables
print(element) # indentation de 4 espaces, on est dans le bloc de code
element = element + 2
print ("après modification ",element)
print("fini") # l'indentation a cessé, on a quitté le bloc
print (ma_liste)
Le programme se déroule comme suit : 1 après modification 3 2 après modification 4 5 après modification 7 10 après modification 12 50 après modification 52
Et pourtant, à la fin print(ma_liste) montre que la liste est inchangée ! La ligne " element = element + 2" a bien modifié l'élément extrait de la liste, mais n'a pas modifié la liste...)
On peut vérifier sur cette capture d'écran Python Tutor que au cours de la 2e itération la variable element qui valait 2 (2e élément de la liste) est bien passée de 2 à 4, mais que cela n'a pas du tout modifié la liste originale.
Une manière de transformer la liste en ajoutant deux consiste à recréer une liste :
ma_liste = [1, 2, 5, 10, 50] # une liste d'entiers
ma_liste_modifiée = []
for element in ma_liste:
print(element)
ma_liste_modifiée.append(element + 2) # j'ajoute les éléments un à un à la nouvelle liste
print("fini")
ma_liste = ma_liste_modifiée # je fais pointer ma_liste vers la nouvelle liste
...ce qui peut s'écrire de manière plus compacte par une "comprehension list" (liste en intension) :
ma_liste = [1, 2, 5, 10, 50] # une liste d'entiers
ma_liste = [element + 2 for element in ma_liste ]
Si je veux parcourir une liste avec le rang : enumerate
ma_liste = [1, 2, 5, 10, 50] # une liste d'entiers
for rang, element in enumerate(ma_liste):
print("L'élement de rang ",rang, " est :", element)
L'élement de rang 0 est : 1 L'élement de rang 1 est : 2 L'élement de rang 2 est : 5 L'élement de rang 3 est : 10 L'élement de rang 4 est : 50
Hé oui, les indices commencent à zéro !
Parcourons maintenant deux listes en parallèle, en prenant à chaque itération un élément de même rang, comme une fermeture éclair parcourt deux bords crantés : zip
ma_liste_numeros = [1, 2, 5, 10, 50] # une liste d'entiers
ma_liste_mots = [ "un", "deux", "cinq", "dix", "cinquante" ]
for numero, mot in zip(ma_liste_numeros, ma_liste_mots):
print("Le numéro ",numero, " s'écrit :", mot)
Le numéro 1 s'écrit : un Le numéro 2 s'écrit : deux Le numéro 5 s'écrit : cinq Le numéro 10 s'écrit : dix Le numéro 50 s'écrit : cinquante
A3) Les fonctions
La fonction est un objet comme les autres (ou presque) ! Les fonctions et méthodes (fonctions définies dans une classe) peuvent être appelées pour exécuter des instructions et retourner un ou plusieurs objets. Techniquement, ce sont donc des « callables » (http://sametmax.com/quest-ce-quun-callable-en-python/ ).
A.3.1. Rappels, fonction et paramètres de la fonction
Une fonction (ou une méthode) est définie par le mot-clé def, un nom de fonction suivi d'une liste de noms de variables entre parenthèses, puis un bloc de code introduit par les ":", qui commence de préférence par une docstring suivie d'une ligne blanche.
def ma_fonction(param1, param2, param3):
""" doctring pour décrire ce que fait ma_fonction """
Cas particulier des méthodes, avec le mot-clé selfpour renvoyer à l'instance courante (attention, tout autre mot fait l'affaire, dont si vous l'oubliez le premier nom fourni fera office de "self" !).
On remarque que dans le cas de la fonction __init__
qui initialise l'instance, les attributs n'ont pas forcément le même nom que les paramètres, et peuvent d'ailleurs être en nombre différent !
class MonObjet:
""" Cette classe est là car il en faut une ; un docstring au format google
Attributes:
class_attribute (str): (class attribute) parce que j'ai envie de mettre un argument de classe
param1 (float): un attribut d'instance
somme (float): somme de deux valeurs fournies dans le __init__
"""
class_attribute = "la classe, cet attribut"
def __init__ (param1, param2, param3):
self.param1 = param1
self.somme = param2 + param3
def ma_methode(self, a, b):ab:
""" docstring au format PEP : J'ajoute a et b à self.somme.
:param a : un truc à ajouter
:type a : entier
:param b : un autre truc à ajouter
:type a : flottant ou entier
:return: somme a + b
:rtype: flottant
"""
aplusb = a + b
self.somme += aplusb
return aplusb
On peut remplacer self par this, comme en java ; ce n'est pas du tout recommandé, mais on insiste sur l'importance de ne pas l'oublier !
def ma_methode(this, a, b):
""" petite description de ma fonction : elle calcule a + b, et l'ajoute à l'attribut somme ; elle retourne aussi a+b """
aplusb = a + b
this.somme += aplusb
return aplusb
machin = MonObjet(4,5,6)
bidule = machin.ma_methode(1,2)
Un point de vocabulaire : paramètres et arguments
En Python vous trouverez ces deux termes. Pour la documentation des fonctions (les "docstrings"), qui est une bonne pratique recommandée dans les PEPs, sphinx "paramètres" de les définir comme :param nom_variable:
(Sphinx ) tandis que le format Google utilise Argas... Attributes:
En théorie, les paramètres sont ce qui est déclaré dans la fonction, entre les parenthèses, et les arguments sont ce qui est passé lors de l'appel de la fonction.
Donc, ici a et b seraient les paramètres de ma_methode
et 1,2 seraient les arguments passés lors de l'appel.
La méthode ma_methode
renvoie ici un nombre calculé par la fonction, qui est également décrit dans la docstring.
quand le curseur est positionné entre les parenthèses, Ctrl+P fait apparaître la liste des paramètres attendus.
def ma_fonction(param1:str, param2:int, param3:float) -> float:
Attention, les annotations sont des indications ; votre IDE ou votre débuggueur peuvent détecter des anomalies, mais une définition incorrecte d'annotation n'empêchera pas vos code de tourner, par contre cela peut compliquer le travail de quelqu'un qui essaierait de comprendre le code...
La manière de définir et d'ordonner les paramètres est décrite dans le point f ci-dessous
A.3.2. Rappel des bases pour éviter des erreurs courantes :
Gardez en tête que la fonction est un objet ! Cela aide à bien comprendre les points importants suivants, qui seront détaillés ensuite.
a) Attention à mettre les parenthèses quand on appelle la fonction et seulement là !
ma_fonction # = objet, de type function
ma_fonction() # = appel à la fonction (elle s’exécute)
truc= ma_fonction # truc est une fonction, en fait truc se comporte maintenant comme ma_fonction
truc= ma_fonction() # j'exécute ma_fonction et truc est ce que renvoie ma_fonction
b) une fonction retourne toujours quelque chose, si rien n’est précisé par défaut return = None je peux récupérer tout ce qu’elle retourne dans une variable, ici appelée resultat, qui est éventuellement un tuple contenant plusieurs objets ;
resultat = ma_fonction() # éventuellement resultat = None ou un *tuple*
def ma_fonction(x):
# définition d'une fonction prenant en argument x ; je ne précise pas son type
print(x)
return x, 2*x, x**2 # la fonction renvoie un tuple de 3 éléments
# j'appelle la fonction
resultat = ma_fonction(5) # ici la fonction renvoie un tuple de 3 éléments
print (resultat) # donc la variable resultat est un tuple de 3 éléments
L'exécution du code donne :
(5, 10, 25)
Ma variable resultat est un tuple autrement dit resultat = (5, 10, 25) Un tuple est u conteneur, comme list mais avec des caractéristiques différentes. Pour récupérer tous les termes on « déballe » le tuple (unpacking) :
(x,y,z) = resultat ou x,y,z = resultat.
On aura dans les 2 cas x=5 ; y=10 et z=25
Si on n’a besoin que du premier terme : on peut se contenter d'écrire x, le_reste = resultat
Vous pourrez vérifier que dans ce cas le_reste est un tuple équivalent à (y,z), c'est logique !
Mais on peut faire encore plus succinct, pour bien montrer que le reste ne nous intéresse vraiment pas :
x, _ = resultat ou même x, = resultat
( _ = nom de variable valide (!) , mais par convention on sait qu'on ne s’en servira pas )
Si aucune variable ne récupère le résultat ce n’est pas gênant mais ce qui a été retourné est « perdu » a priori car on n'a pas affecté de nom ; plus exactement on ne peut agir dessus (on pourrait épiloguer un peu plus, mais pour débuter cela suffira).
C’est le cas pour certaines fonctions matplotlib où on peut écrire plt.plot(x,y) (je trace juste une courbe) ou ma_courbe, = plt.plot(x,y) (en plus je nomme ma courbe, premier terme du tuple, je n’ai pas besoin des autres)
c) portée des variables :
On a dit plus haut que si j'exécute une fonction sans récupérer le résultat dans une variable, elle s'exécute normalement (écrire une ligne dans un fichier ou la console, tracer un graphique, faire une calcul...) mais je ne récupère pas ce qu'elle renvoie éventuellement avec return.
Un exemple où l'on essaie de définir des variantes de variables existantes à l'intérieur d'une fonction ; n'hésitez pas à exécuter ces instructions dans Python Tutor et à les triturer :
# ici on est dans le corps principal
annonce = "ici on parle français"
salutation = "bonjour"
c = "Chopin"
print(salutation)
ma_liste_numeros = [1, 2, 5, 10, 50]
ma_liste_mots = [ "un", "deux", "cinq", "dix", "cinquante" ]
# définition d'une fonction qui va avoir son "propre espace"
def ma_fonction_pl(salutation):
print("Nous sommes dans la fonction, on a passé en argument ", salutation)
salutation= "dzień dobry" # on change la valeur de la variable passée en argument
print(salutation) # on vérifie que le changement est effectif
annonce = "teraz mówimy po polsku" # on change la valeur de la variable préexistante annonce
print(annonce)
print(c) # comme il n'y a pas de c dans la fonction, Python va aller chercher au-delà
ma_liste_numeros.append(100) # je MODIFIE la liste EN PLACE par une méthode
ma_liste_mots = [ "jeden", "dwa", "pięć", "dziesięć", "pięćdziesiąt", "sto" ] # j'écrase cette liste
for numero, mot in zip(ma_liste_numeros, ma_liste_mots):
print("Le numéro ",numero, " s'écrit :", mot)
# remarque : il n'a pas le mot clef return : CETTE FONCTION RETOURNERA None
# de retour dans le corps du programme, j'appelle la fonction
resultat = ma_fonction_pl(salutation)
print (resultat) # on n'a pas explicité 'return' donc la fonction renvoie None
print("Nous sommes sortis de la fonction, on a passé en argument ", salutation)
print (annonce ) # la fonction n'a modifié ni annonce ni argument_bidon
print(ma_liste_numeros) # la modification par *append* a été prise en compte
print(ma_liste_mots) # la liste n'a pas été modifiée
Le code ci-dessus affiche : bonjour Nous sommes dans la fonction, on a passé en argument bonjour dzień dobry teraz mówimy po polsku Chopin Le numéro 1 s'écrit : jeden Le numéro 2 s'écrit : dwa Le numéro 5 s'écrit : pięć Le numéro 10 s'écrit : dziesięć Le numéro 50 s'écrit : pięćdziesiąt Le numéro 100 s'écrit : sto None Nous sommes sortis de la fonction, on a passé en argument bonjour ici on parle français [1, 2, 5, 10, 50, 100] ['un', 'deux', 'cinq', 'dix', 'cinquante']
Sur la Copie d'écran de Python tutor, code en cours d'exécution à pas à pas, on remarque qu'il y a en mémoire 2 versions différentes de ma_liste_mots, dont une dans l'espace de nommage de la fonction, qui a été créée par l'instruction ma_liste_mots=[ ...]. La liste ma_liste_numeros n'existe en revanche qu'en un seul exemplaire.
d) une méthode modifie l’objet en place ou en renvoie un nouveau selon le cas… Bien vérifier dans la doc comment se comporte la méthode que l’on utilise ! ! ! Selon les cas, une méthode peut :
- modifier un objet (exemple append) ; en général elles retournent « None »
- retourner un nouvel objet sans modifier le premier
- laisser le choix (argument inplace=True ou =False) :
Exemples courants pour comprendre méthode sort vs fonction sorted
# méthode sort = tri « en place » / « in place »
ma_liste.sort() # ma_liste est maintenant triée
# fonction sorted : crée un nouvel objet, trié
ma_liste_triee = sorted(ma_liste)
np.asarray vs np.array
import numpy as np
# nouvel objet = vecteur numpy avec valeurs de la liste
np_mes_valeurs = np.array(ma_liste_de_valeurs) # ma_liste_de_valeurs est toujours de type *list*
# transformer ma_liste_de_valeurs en vecteur numpy
np.asarray(ma_liste_de_valeurs) # ma_liste_de_valeurs est maintenant un numpy.array
Parfois, un mot-clef permet de choisir ; voir par exemple dans la bibliothèque Pandas pandas.DataFrame.drop DataFrame.drop(labels=None, axis=0, index=None, columns=None, level=None, inplace=False, errors='raise')[source] Drop specified labels from rows or columns. *Parameters * (...) inplace bool, default False If False, return a copy. Otherwise, do operation inplace and return None. Returns DataFrame (DataFrame without the removed index or column labels) : sauf si inplace=True.
e) une fonction peut être passée en argument d’une autre fonction
fonction_avec_options(var1, var2, ma_fonction)
f)
Les cas simples sont intuitifs, mais on propose ici un topo détaillé pour bien comprendre les différentes natures d'argument.
Sources:
Les paramètres soivent être absolument placés dans cet ordre : - paramètres normaux et obligatoires; - paramètres normaux facultatifs (valeur par défaut); - paramètres dynamiques; - paramètres dynamiques nommés.
# exemple éhontément piqué à Sam et Max
def affichage_hybride(parametre_normal,
parametre_avec_default="valeur par défaut",
*args,
**kwargs):
print(parametre_normal)
print(parametre_avec_default)
print(args)
print(kwargs)
Ce qui va donner à l'appel de fonction suivant :
affichage_hybride("param1", "param2", "infini1", "infini2", kwinfini1=1, kwinfini2=2)
param1
param2
('infini1', 'infini2')
{'kwinfini1': 1, 'kwinfini2': 2}
On va expliquer tout ça dans la suite, et vous pouvez également regarder les 2 tutos mis en ref ci-dessus. Et surtout, MANIPULEZ
Pour un utilisateur de fonctions codées par d'autres, les méthodes de matplotlib par exemple, on n'a pas forcément besoin de ce niveau de détail, mais c'est quand même bien de savoir quand l'ordre est important (en premier des arguments de position, obligatoires, et tout à la fin les arguments passés par mot-clef). Les arguments nommés avec valeur par défaut et les arguments passés par dictionnaire se ressemblent beaucoup pour un utilisateur qui ne "rentre" pas dans le code, mais il faut savoir que là encore l'ordre est important. Vous reviendrez à ces notions en développant vous-mêmes.
le plus simple : un nombre fini d'arguments "de position" Si vous définissez un nombre fini d'arguments, il suffit de les passer dans cet ordre lors des appels de fonction, sans rappeler leur nom.
# définition d'une fonction avec 2 arguments
def ma_fonction(a, b):
message = f"{a} divisé par {b} = {a/b}" # au passage, une " f string"
return message
print (ma_fonction(2,5)) # je n'ai pas précisé les noms, leur position suffit
print (ma_fonction(b=5,a=2)) # si je précise les noms, je peux intervertir les arguments
pour mémoire : dans les versions récentes de Python, on peut forcer un argument à être "de position" et uniquement de position (on ne peut pas le définir par son mot clé)
nombre fini d'arguments facultatifs, avec valeur par défaut Les arguments avec valeur par défaut permettent d'alléger les appels, mais comme on peut en avoir plusieurs qui soient facultatifs il faut préciser les noms lors des appels pour que l'interpréteur s'y retrouve. De même, ils doivent toujours être définis après les arguments de position.
# définition d'une fonction avec un 3e argument avec valeur par défaut
def ma_fonction(a, b, nb_decimales=2):
# on en profite pour utiliser une " f string"
# pour écrire mon_chiffre avec deux chiffres après la virgule on écrit {mon_chiffre:.2f}
# mais je peux aussi passer une variable, entre {}, comme nombre de décimales
message = f"{a} divisé par {b} = {a/b:.{nb_decimales}f}"
return message
print (ma_fonction(2,5)) # je n'ai pas précisé nb_decimales donc nb_decimales vaut la valeur par défaut
print (ma_fonction(2,5, nb_decimales=5 )) # je précise nb_decimales
print (ma_fonction(b=5,a=2, nb_decimales=5 )) # si je précise les noms, je peux intervertir les arguments "de position"
2 divisé par 5 = 0.40 2 divisé par 5 = 0.40000 2 divisé par 5 = 0.40000
nombre indéfini d'arguments args avec le splat ()
La fonction print est un exemple de fonction qui prend un nombre indéfini d'arguments : on peut écrire print("bonjour")
, print("le résultat est", resultat)
, print("si je divise ",a, " par ", b, " j'obtiens ", resultat)
Si on veut admettre un nombre non défini d'arguments, on peut bien sûr faire passer un itérable (liste, tuple...) et dans la fonction on va itérer sur les éléments.
Il existe une autre façon (plus "pythonesque" ??) de faire : avec une étoile précédant l'argument, pour préciser que c'est en fait un tuple que la fonction saura 'déballer' (unpacking) et interpréter.
Par convention, on note ce "tuple d'arguments" *arg) et on ne peut y recourir qu'une seule fois, sinon l'interpréteur ne s'y retrouverait pas (dans les arguments passés, où commencerait la deuxième série ??), dans ce cas il faut travailler avec un argument = un tuple, à passer comme tel.
# définition d'une fonction avec un nombre indéfini d'arguments
def somme(*un_tuple_de_nombres):
somme = 0
for nombre in un_tuple_de_nombres:
# on frime avec une notation qui ressemble au c ;
# c'est surtout plus rapide car on modifie "en place" ;
somme += nombre # équivalent à somme = somme + nombre
return somme
print(somme(1,2))
print(somme(1,2,3,4,5,6))
# je ne peux pas passer directement un itérable mais je peux en forcer l'unpacking
un_tuple = range(10) # je peux travailler avec n'importe quel type d'itérable : liste...
print(somme(*un_tuple )) # si j'oublie le * j'aurais un "TypeError"
ma_fonction(arg_de_position,*, arg_nomme)
ma_fonction_avec_seulement_des_arguments_nommes(*, arg_nomme_1, arg_nomme_2)
nombre indéfini d'arguments **kwargs : nombre indéfini de clés et valeurs
Avec *arg, on peut faire passer une et une seule fois un nombre indéfini d'arguments de même nature, ou au moins susceptibles d'être compris et traités par la fonction (print accepte des string et des nombres même mélangés par exemple).
Si on veut admettre un nombre non défini d'arguments de types différents et facultatifs, on peut bien sûr les définir en paramètre avec défaut, mais on peut aussi prévoir dans le code de considérer une suite d'arguments nommés comme un dictionnaire, dont les clés et les valeurs seront traitées dans le code.
Par convention, on note ce "dictionnaire d'arguments" **kwarg).
Le module matplotlib recourt énormément aux kwargs, peut-être parce qu'il recourt à un grand nombre d'attributs partagés par différents objets. Par exemple : ax.plot(x,y,color='red', linewidth=5) ; on peut définir des couleurs et des largeurs de ligne pour un grand nombre d'objets,
plus avancé... pour mémoire
g) une fonction peut avoir des attributs (c’est ce qui permet la définition de décorateurs…) Il y a beaucoup de similitudes entre classes et fonctions, mais ceci dépasse le cadre actuel de ces notes.
A4 Les dictionnaires
Définition d'un dictionnaire
Un dictionnaire est un conteneur qui contient des clés, et à chaque clé est associée une valeurs. Pour que cela ait un sens, il faut que la clé ne puisse pas changer en cours de route : elle doit être d'un type * non mutable* La valeur peut être une variable simple ou un conteneur ou une instance de classe...
création d'un dictionnaire ; entre accolades
De la même façon que les crochets [ ] sont associés aux objets de type list, les dictionnaires sont délimités par des accolades { }.
dictionnaire_main = {1:"un", 2:"deux", 3: "trois", 4:"quatre", 5: "cinq"} # dictionnaire avec couples clé: valeur
mon_dictionnaire = dict() # dictionnaire vide
ceci_n_est_pas_un_dictionnaire_mais_un_set = {1, 2, 5, 10, 50} # pas de couples clé, valeurs...
dico_chiffres = dict(zip(ma_liste_numeros, ma_liste_mots)) # si les 2 ont bien le même nombre d'éléments...
mon_dictionnaire [clef] = valeur # j'ajoute une entrée (j'ajoute des entrées comme je veux)
la variable clef doit être d'un type NON-MUTABLE (voir notes) ; les types string, datetime, chiffre... conviennent la variable valeur peut être de n'importe quel type, y compris des conteneurs
mon_dictionnaire[clef] #renvoie la valeur qui correspond à la clé clef
mon_dictionnaire.keys() # vue des clés ; ce n'est pas une liste mais c'est un itérable
mon_dictionnaire.values() # vue des valeurs; ce n'est pas une liste mais c'est un itérable
mon_dictionnaire.items() # vue des couples clés-valeurs ; ce n'est pas une liste mais c'est un itérable
six = 6
six in dictionnaire_main.keys() # booléen, vrai si cle_de_sol est une ces clés
Pour terminer, plusieurs manières de faire afficher les couples clé/valeurs :
for clef in dictionnaire_main.keys() :
print("clé = ", clef, " valeur = ",dictionnaire_main [clef])
# il y a plus simple :
for clef in dictionnaire_main :
print("clé = ", clef, " valeur = ",dictionnaire_main [clef])
# et encore plus simple :
for clef, valeur in dictionnaire_main.items() : # à chaque itération on a un tuple clef, valeur
print("clé = ", clef, " et valeur = ", valeur)
# pour jouer avec les tuples :
for tuple_clef_valeur in dictionnaire_main.items() : # à chaque itération on a un tuple clef, valeur ; ici on le garde sous forme de tuple pour comprendre ce qui se passe
clef, valeur = tuple_clef_valeur # on "déballe" le tuple : UNPACKING
print("clé = ", clef, " et valeur = ", valeur)