Introduction à l'Algorithmie et à Python III

📌 Ecole nationale des chartes, Master TNAH, 2020


Alix Chagué

📫 alix.chague@inria.fr

💼 Ingénieure Recherche et Développement @ Inria

inria inria

Crédits

Librement inspiré des supports de cours de Gaël Guibon
Librement inspiré des supports de cours de Julien Pilla

Retrouver l'ensemble du cours sur 👉 github.com/alix-tz/enc-intro-algo👈

Syllabus

Récapitulatif

  • variables (nom, affectation de valeur, type)
  • fonctions built-in et mots réservés
  • opérations arithmétiques et retypage
  • comparer des valeurs
  • opérateurs booléens
  • conditions (si... alors)
  • répétition (tant que / répéter x fois)

Plan du cours

  • Syllabus
  • Fonctions
  • Import et modules
  • Nouveau type : listes
  • Nouveau type : dictionnaires (et tuples)
  • Aller plus loin

Liens utiles

👉 Simulateur d'environnement Python en mode pseudo-IDE : https://repl.it/languages/python3

👉 Simulateur d'environnement Python en mode console : https://www.python.org/shell/

👉 Documentation officielle de Python : https://docs.python.org/3/

👉 Visualisateur d'exécution de code Python : http://pythontutor.com/

Quelques ressources pour continuer à se former

👉 "Automate the Boring Stuff with Python" (en) : https://automatetheboringstuff.com/

👉 Leçons dédiées à Python sur Programming Historian (en, fr ou es) : https://programminghistorian.org/en/lessons/introduction-and-installation

👉 "Apprendre à coder avec Python", MOOC de l'Université Libre de Bruxelles (fr) : https://www.fun-mooc.fr/courses/course-v1:ulb+44013+session04/about

👉 "Apprenez à programmer en Python", cours en ligne sur OpenClassroom (fr) : https://openclassrooms.com/fr/courses/235344-apprenez-a-programmer-en-python

👉 "Introduction à Python" pour le Master Ingénierie Multilingue de l'Inalco, Loïc Grobol et Yoann Dupont (fr) : https://loicgrobol.github.io/python-im/m2-2018/

Fonctions

Une fonction est une suite d'instructions rassemblées sous un nom et que l'on peut appeler dans un programme. On dit qu'une fonction renvoie ou retourne une valeur une fois que l'ensemble des instructions qu'elle contient ont été exécutées. Cette valeur peut être nulle (None) ou essentielle à l'exécution du reste du programme.

On a vu qu'il existe des fonctions built-in (ex : print()), mais l'utilisateur-rice peut aussi créer ses propres fonctions, souvent pour simplifier le code en créant des unités logiques.

Pour créer une fonction, on utilise le mot-clef def suivi du nom que l'on souhaite donner à la fonction, suivi de parenthèses puis de deux points (():). Pour mettre fin à la déclaration de la fonction, on utilise le mot-clef return. Il peut être suivi d'une valeur ou être utilisé seul

Les instructions décrites à l'intérieur d'une fonction ne sont pas exécutées tant que la fonction n'est pas appelée. Pour appeler une fonction on utilise son nom, suivi de parenthèses. Une fonction peut appeler une autre fonction.

Exemple 1

Je crée une fonction nommée dit_coucou dont le seul but est d'afficher "Coucou !".

In [6]:
# je crée une fonction
def dit_coucou():
    print("Coucou !")
    return
    
# j'appelle la fonction
dit_coucou()
Coucou !

Précision sur les renvois

En Python, les trois déclarations suivantes mènent au même résultat :

In [7]:
def dit_coucou():
    print("Coucou !")
    return None

def dit_coucou():
    print("Coucou !")
    return

def dit_coucou():
    print("Coucou !")

Cela signifie qu'en Python, le mot-clef return n'est pas obligatoire si l'on ne renvoie pas de valeur à la fin de la fonction. En fait, la fin de l'indentation suffit à signaler la fin de la déclaration de la fonction.

Quand une fonction renvoie une valeur, on peut la stocker dans une variable pour l'utiliser ensuite.

In [8]:
def combien_font_trois_plus_trois():
    return 3 + 3

resultat = combien_font_trois_plus_trois()  # on peut stocker le résultat
print(combien_font_trois_plus_trois())  # ou interagir avec directement
6

Arguments

Les parenthèses ne sont pas ici pour faire joli. Comme avec les fonctions en mathématiques, elles servent à faire passer une ou plusieurs valeurs (arguments ou paramètres) qui seront utilisées pour exécuter les instructions définies dans la fonction.

Les arguments sont en quelque sorte des variables qui n'existent que durant le temps d'exécution de la fonction. Pour désigner ce phénomène, on parle de la portée (scope) d'une variable.

In [9]:
def dit_moi_coucou(nom):
    print("Coucou", nom, "!")
    
mon_prenom = "Alix"
dit_moi_coucou(mon_prenom)
Coucou Alix !

Si j'essaie de faire appel à la variable nom en dehors du bloc de définition de la fonction "dit_moi_coucou", ça ne fonctionne pas

In [10]:
print(nom)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-10-c427005c1d14> in <module>
----> 1 print(nom)

NameError: name 'nom' is not defined

Récapitulons les concepts

On a parlé de définition de fonction, d'appel de fonction, d'arguments, de renvois et de scope.

illustration de la définition d'une fonction

Exemple 2

In [11]:
def afficher_lavariable_son_type_et_sa_valeur(lavariable):
    print("la variable", type(lavariable), lavariable, sep=" | ")
    return "C'est fait!"

le_nom_de_mon_chat = "Query"
print(afficher_lavariable_son_type_et_sa_valeur(le_nom_de_mon_chat))
la variable | <class 'str'> | Query
C'est fait!
  1. Je crée une fonction, dont le nom est très (trop) explicite, intitulée afficher_lavariable_son_type_et_sa_valeur()
    • Cette fonction reçoit une valeur dans une variable nommée lavariable
    • La fonction prévoit l'affichage de la chaîne de caractères "la variable", du type de la valeur contenue dans la variable lavariable, puis de cette valeur, le tout séparé par "|'
    • Arrivée à la fin des instructions prévues dans cette fonction, la fonction retournera une chaîne de caractères contenant "C'est fait!"
  2. Je crée une variable le_nom_de_mon_chat qui contient la chaîne de caractères "Query"
  3. Je passe la valeur contenue dans la le_nom_de_mon_chat à la fonction afficher_lavariable_son_type_et_sa_valeur() et j'affiche la valeur retournée

À quel moment choisit-on de définir une fonction ?

Il n'y a pas de règle qui définisse qu'à tel ou tel moment il faut créer une fonction. C'est une affaire de pratique, de choix dans l'organisation du code et de lisibilité de celui-ci.

Souvent, on fait une fonction pour un ensemble logique d'actions.

Par exemple dans un programme qui génère des tweets illustrés, une fonction crée un texte et une image, une fonction enregistre l'image localement, une fonction envoie le texte et l'image sur Twitter. Décomposer ainsi le code permet de faire ressortir les grandes étapes de traitement.

Souvent, aussi, on crée une fonction que l'on réutilisera par la suite dans d'autres projets, on évite ainsi de recréer un élément du code qu'on a déjà rédigé.

Par exemple, une fonction pour lister l'ensemble des fichiers contenus dans un dossier.

Organisation du code

Pour faciliter la lecture d'un script, on rassemble l'ensemble des définitions de fonction avant de passer au groupe d'instruction principal, qui contiendra les appels de fonctions.

# définition de la fonction 1
# définition de la fonction 2
# définition de la fonction 3
# --------
# instructions
# appel de la fonction 1
# instructions
# appel de la fonction 1
# appel de la fonction 3
# instructions
# appel de la fonction 2

Exercices pratiques

Faire les 5 exercices du fichier fonctions.py

👉 https://repl.it/@AlixChagu/ENCintroalgo#basics/fonctions.py

Libraries, imports et modules

Importer des fonctions externes

À part les fonctions built-in et les fonctions créées de toutes pièces, il est possible d'utiliser des fonctions déjà définies par d'autres développeur·ses.

Pour faire simple, ces fonctions sont enregistrées dans des libraries (ou package) que l'on importe dans un programme pour pouvoir s'en servir. On parle aussi de dépendances.

Certaines libraries sont built-ins : elles sont toujours disponibles, il suffit de les activer. D'autres doivent être installées avant de pouvoir être activées. On utilise pour cela un gestionnaire de paquets (comme pip).

Quelques exemples de libraries built-in que vous rencontrerez rapidement : os, csv, random.

Pour activer une library, il suffit de l'importer au tout début du script avec le mot-clef import, suivi du nom de la library.

In [12]:
import os  # on peut désormais utiliser les fonctions de la library os.
import random  # on peut désormais utiliser les fonctions de la library random.

Pour utiliser une fonction issue d'une library, il faut rappeler son nom, puis appeler le nom de la fonction comme on le ferait avec une fonction définie localement.

Cela se fait selon le modèle suivant : nom_du_paquet.nom_de_la_fonction()

In [13]:
# random possède une fonction appelée randint() qui permet de générer un nombre entier au hasard 
# compris entre a et b.
print(random.randint(0, 100))
82

Organisation du code

Pour faciliter la lecture d'un script, les imports se font au tout début du script.

# imports
# --------
# définition de la fonction 1
# définition de la fonction 2
# définition de la fonction 3
# --------
# instructions
# appel de la fonction 1
# instructions
# appel de la fonction 1
# appel de la fonction 3
# instructions
# appel de la fonction 2

Nouveau type : Listes

📜 Les listes sont un type de variable pouvant contenir une ou plusieurs valeurs à la suite.

📜🐍 En Python, une liste peut contenir plusieurs types de variables en même temps.

📜🤨 En fonction des langages de programmation, on les appelle aussi array ou vecteur.

📜 Une liste est un objet appartenant à la catégorie des iterables (comme les chaines de caractères)

📜 Les valeurs contenues dans une liste sont indexées. On peut donc cibler précisément telle ou telle valeur contenue dans la liste.

📜🤯 L'indexation dans une liste commence à 0

Syntaxe

In [14]:
# créer une liste
ma_liste_vide = []
mon_autre_liste_vide = list()
ma_liste = ['A','B','C','D']

# Accéder au contenu d'une liste
print('ma_liste[2]  :', ma_liste[2])
une_variable = ma_liste[1]
print('une_variable :', une_variable)


# Une liste peut contenir plusieurs types... y compris des listes
super_liste = ["ceci n'est pas un chiffre", 3.14, True, type('hello'), 2048, ma_liste]
print('super_liste  :', super_liste)
ma_liste[2]  : C
une_variable : B
super_liste  : ["ceci n'est pas un chiffre", 3.14, True, <class 'str'>, 2048, ['A', 'B', 'C', 'D']]

Indexation

Moyen mnémotechnique : l'indexation des listes fonctionne comme les étages en France.

index 0 1 2 3
valeur A B C D
palier RDC 1E 2E 3E

Si vous demandez un index qui n'est pas associé à une valeur (parce que la liste est trop courte), vous obtenez une erreur (IndexError).

In [15]:
print(ma_liste[4])
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-15-b214c2428d1f> in <module>
----> 1 print(ma_liste[4])

IndexError: list index out of range

Indexation par la fin

On peut viser un emplacement dans la liste en partant du début ou de la fin.

In [16]:
liste_exemple = ["RDC", "1er", "2e", "3e", "4e"]
print(liste_exemple[-1])
print(liste_exemple[-3])
4e
2e
In [17]:
print(liste_exemple[-10])
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-17-c4b99e6a1b85> in <module>
----> 1 print(liste_exemple[-10])

IndexError: list index out of range

Bonnes pratiques

Pour éviter les déconvenues, pensez à utiliser len() sur une liste avant de cibler les valeurs indexées.

In [18]:
print(len(liste_exemple))
5
In [19]:
print(liste_exemple[len(liste_exemple) - 1])
4e
In [20]:
index = 6
if index < len(liste_exemple):
    print(liste_exemple[index])
else:
    print("No can't do!")
No can't do!

Sections, sublists ou encore slices

Si on peut cibler une seule valeur à la fois, on peut aussi cibler une section dans une liste : liste[début:fin:pas]

In [2]:
exemple = ['p','y','t','h','o','n', ' ', '!']
print(exemple[2:6])
['t', 'h', 'o', 'n']
0 1 2 3 4 5 6 7
"P" "Y" "T" "H" "O" "N" " " "!"
- - start - - - stop -
In [3]:
# on peut utiliser des index négatifs
print('1 :', exemple[-6:-2])    
1 : ['t', 'h', 'o', 'n']
In [4]:
# en parcourant la liste de gauche à droite
print('2 :', exemple[-2:-6])  
2 : []
In [5]:
# le pas de 1 est implicite, on peut le modifier
print('3 :', exemple[2:6:2])  
3 : ['t', 'o']
In [6]:
# sans index de fin, on continue jusqu'à la fin de la liste 
print('4 :', exemple[2:])     
4 : ['t', 'h', 'o', 'n', ' ', '!']
In [7]:
# sans index de début, on part du début de la liste
print('5 :', exemple[:5])     
5 : ['p', 'y', 't', 'h', 'o']
In [8]:
# on peut faire une section allant du début à la fin (copie)
print('6 :', exemple[:])      
6 : ['p', 'y', 't', 'h', 'o', 'n', ' ', '!']
In [9]:
# on peut ne préciser que le pas
print('7 :', exemple[::2])    
7 : ['p', 't', 'o', ' ']
In [10]:
# n'importe lequel des éléments peut être implicite
print('8 :', exemple[:5:2]) 
8 : ['p', 't', 'o']

Itérables

Une chaîne de caractères fait aussi partie de la catégorie des itérables, on peut donc se servir des slices et des index sur ce type de valeur.

In [23]:
chaine = 'python !'
print(chaine[len(chaine) - (len(chaine)*2)])
print(chaine[2:6])
p
thon

En revanche, dans une chaîne de caractères, on ne peut pas réassigner une valeur à un index. On dit qu'une chaîne de caractères est immutable.

In [24]:
chaine[4] = '*'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-24-b6a7f31ceb5f> in <module>
----> 1 chaine[4] = '*'

TypeError: 'str' object does not support item assignment
In [25]:
# mais on peut toujours contourner ce problème...
modif = chaine[:3] + '*' + chaine[4:]
print(modif)
pyt*on !

Méthodes des listes

Les listes ont des fonctions et méthodes qui leur sont associées.

  • liste.append(x) : ajouter un élément à la fin de la liste
  • liste.pop({index}) : supprimer un élément de la liste à partir de son index (le récupérer en mémoire)
  • liste.remove(x) : supprimer de la liste la première occurrence de l'élément recherché
  • liste.index(x) : renvoyer l'index de la première occurrence de l'élément recherché
  • liste.count(x) : compter le nombre d'occurrence de l'élément recherché dans la liste
  • liste.sort() : trier la liste selon un ensemble de règles
In [26]:
liste_1a4 = [1, 2, 3, 4]
liste_5a8 = [5, 6, 7, 8]
liste_lettres = ['Q', 'Z', 'S', 'D']

# On peut concaténer 2 listes
liste_1a8 = liste_1a4 + liste_5a8
print(liste_1a8)

# Attention .append() n'a pas le même effet
liste_1a8.append(liste_lettres)
print(liste_1a8)
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8, ['Q', 'Z', 'S', 'D']]

Nouveau type : Dictionnaires

🔑 Les dictionnaires sont un type de variable contenant une série de valeurs associées à des clefs d'accès.

🔑 Contrairement aux listes, les dictionnaires n'ont pas d'index (ce ne sont pas des itérables).

🔑 Comme les listes, les dictionnaires peuvent contenir plusieurs types de données en même temps, y compris d'autres dictionnaires.

🔑 Un dictionnaire peut contenir plusieurs fois la même valeur mais chaque clef doit être unique.

Syntaxe

In [27]:
# créer un dictionnaire
mon_dico_vide = {}
mon_autre_dico_vide = dict()
mon_dico = {"clef" : "valeur"}

print("mon_dico =", mon_dico)

# Ajouter ou modifier des valeurs dans un dictionnaire
print("mon_dico_vide =", mon_dico_vide)
mon_dico_vide["hello"] = "world"
print("mon_dico_vide =", mon_dico_vide)
mon_dico_vide["hello"] = "world!"
print("mon_dico_vide =", mon_dico_vide)

# Supprimer une valeur dans un dictionnaire
del mon_dico_vide["hello"]
print("mon_dico_vide =", mon_dico_vide)
mon_dico = {'clef': 'valeur'}
mon_dico_vide = {}
mon_dico_vide = {'hello': 'world'}
mon_dico_vide = {'hello': 'world!'}
mon_dico_vide = {}
In [28]:
# Accéder au contenu d'un dictionnaire
print('mon_dico["clef"] =', mon_dico["clef"])

# Un dictionnaire peut contenir plusieurs types... y compris des dictionnaires
super_dico = {"chiffre": 42, 113: "entier", "clef3" : [1,2,3]}
print('super_dico  :', super_dico)
mon_dico["clef"] = valeur
super_dico  : {'chiffre': 42, 113: 'entier', 'clef3': [1, 2, 3]}

Dans certains cas, utiliser un dictionnaire plutôt qu'une liste permet de naviguer plus facilement entre les données qu'il contient.

identité = ["Berthe", "Morisot", 1865, 1841, 1895]
identité = {"prénom": "Berthe", "nom": "Morisot", "debut": 1865, "naissance": 1841, "mort": 1895}

Méthodes des dictionnaires

  • dico.keys() : créer un objet itérable contenant la liste des clefs utilisées dans le dictionnaire
  • dico.values() : créer un objet itérable contenant la liste des valeurs contenues dans le dictionnaire
  • dico.get() : envoie la valeur associée à la clef recherchée ou une valeur par défaut si la clef n'existe pas
  • dico.items() : renvoie le contenu du dictionnaire sous la forme d'une liste de tuples.
In [29]:
dico_demo = {"prénom": "Berthe", "nom": "Morisot", "debut": 1865, "naissance": 1841, "mort": 1895}
In [30]:
# .keys()
print(dico_demo.keys())
print(type(dico_demo.keys()))
for key in dico_demo.keys():
    print(key)
dict_keys(['prénom', 'nom', 'debut', 'naissance', 'mort'])
<class 'dict_keys'>
prénom
nom
debut
naissance
mort
In [31]:
# .values()
print(dico_demo.values())
print(type(dico_demo.values()))
for value in dico_demo.values():
    print(value)
dict_values(['Berthe', 'Morisot', 1865, 1841, 1895])
<class 'dict_values'>
Berthe
Morisot
1865
1841
1895
In [32]:
# .get(key, default)
print(dico_demo.get('prénom'))
print(dico_demo.get('métier'))
print(dico_demo.get('métier', 'métier inconnu'))
Berthe
None
métier inconnu
In [33]:
# .items()
print(dico_demo.items())
print(type(dico_demo.items()))
for duo in dico_demo.items():
    print(type(duo), duo, sep=" ; ")
dict_items([('prénom', 'Berthe'), ('nom', 'Morisot'), ('debut', 1865), ('naissance', 1841), ('mort', 1895)])
<class 'dict_items'>
<class 'tuple'> ; ('prénom', 'Berthe')
<class 'tuple'> ; ('nom', 'Morisot')
<class 'tuple'> ; ('debut', 1865)
<class 'tuple'> ; ('naissance', 1841)
<class 'tuple'> ; ('mort', 1895)

Exercices pratiques

Réaliser les 5 exercices du fichier listes_et_dico.py

👉 https://repl.it/@AlixChagu/ENCintroalgo#basics/listes_et_dico.py

Aller plus loin

  • Listes en compréhension
  • Programmation modulaire
  • JSON et CSV
  • Manipulation de fichiers
  • Documenter son code
  • Tests et exceptions
  • Classes et programmation orientée objets

FIN

Prof Chen: "Un tout nouveau monde de rêves, d'aventures et de Python t'attend! Dingue!"