Introduction à l’algorithmique et à la programmation en Python
Table of Contents
1. Algorithmes, fonctions et variables
1.1. Algorithmes
Un algorithme est une séquence d’instructions non ambigüe. Il est destiné à être exécuté par une machine capable de comprendre ces instructions en vu de résoudre un problème.
Un algorithme prend une, aucune ou plusieurs entrées et renvoie une sortie. Les entrées sont les paramètres inconnus et variables du problème que l’on cherche à résoudre. La sortie est le résultat du calcul de l’algorithme, répondant au problème posé.
Un exemple d’algorithme pour ranger ses chaussettes propres.
Algorithme Rangement de chaussette
Entrées : Un ensemble de chaussettes propres \(\cal C\)
Sortie : Un tiroir de chausettes par paire et un bac de chaussettes solitaires
soit \(\cal T\) un tiroir vide, \(\cal B\) un bac vide
tant que il a une chaussette, \(c\) dans \(\cal T\) faire
\(~~~~\) si \(c\) a sa jumelle, \(c'\) dans \(\cal B\) alors
\(~~~~\) \(~~~~\) regrouper \(c\) et \(c'\) dans un paire \(p\)
\(~~~~\) \(~~~~\) ranger \(p\) dans \(\cal T\)
\(~~~~\) sinon
\(~~~~\) \(~~~~\) mettre \(c\) dans \(\cal B\)
\(~~~~\) fin du si
fin du tant que
renvoyer \(\cal T\) et \(\cal B\)
1.2. Programmes
Un ordinateur est modélisé par une unité de calcul et une
mémoire.
L’unité de calcul exécute les instructions d’un algorithme.
Les instructions une fois exécutée on pour but d’agir sur la mémoire
de l’ordinateur.
Un programme informatique est une séquence d’instructions destinée à être exécutée par un ordinateur et écrite dans un langage de programmation, c’est à dire un langage qui peut être interprété par l’ordinateur.
L’opération consistant à passer d’un algorithme (abstrait) à un programme informatique (concret) s’appelle l’implémentation.
On utilisera le langage de programmation Python dans ce cours.
Voici une implémentation en Python de l’algorithme ci-dessus.
def ranger_chaussettes(C) : T, B = {}, {} for c in C : c_paire = paire(c) if c_paire in B : B.remove(c_paire) T.append((c,c_paire)) else : B.append(c) return (T,B)
1.3. Objectifs
L’objectif du cours d’informatique est double : - Savoir concevoir, analyser et valider un algorithme. - Savoir implémenter un algorithme et tester votre implémentation.
1.4. Objets
Objets : Les objets en Python constitue les données manipulables par
les programmes. Les objets standars sont les entiers, les flottants
(nombres réels), les booléens (vrai ou faux), les chaînes de
caractère (texte). Respectivement en Python ils sont noté int
,
float
, bool
et str
.
Les entiers s’écrivent naturellement en Python et supportent les opérations arithmétiques suivantes :
10 + 2, 16 - 4, 3 * 4, 25 // 2, 112 % 25, 12 ** 12
12 | 12 | 12 | 12 | 12 | 8916100448256 |
Les nombres réels sont représentés par des objets de type flottant en Jupyter-Python.
On peut définir les objets flottant comme ceci :
12.2, -12.5, 12., 12.12e12, 12.12e-12
12.2 | -12.5 | 12.0 | 12120000000000.0 | 1.212e-11 |
Les flottants supportent les mêmes opérations arithmétiques que les
entiers.
À noter la différence de notation pour la division réelle.
6. + 6., 6. + 6, 6 * 2., 25 / 2, 12. ** 2
12.0 | 12.0 | 12.0 | 12.5 | 144.0 |
Précision des flottants : 16 chiffres significatifs
1234567890.12345678 - 1234567890.1234567
0.0
1.5. Variables
Une variable est un nom que l’on associe à un objet particulier.
variable = 12
Le processus d’association d’une variable à un objet est appelé affectation.
variable + 21
33
Lors de l’évaluation d’une expression la variable est remplacée par l’objet qui lui est associé
L’objet n’est pas modifié par l’opération précédente. La variable est donc toujours associée au même objet.
variable
12
Lors d’une affectation, la partie droite de l’affectation est d’abord évaluée, puis le résultat est associée à la variable
1.6. Fonctions
L’implémentation d’un algorithme en Python prend la forme d’une
fonction.
Comme en mathématique une fonction en python prend des paramètres qui
sont les entrées de la fonction.
Comme en mathématique une fonction en python renvoie une sortie.
def fonction(x,y) : return x * x + y * y
Les entrées de la fonction sont des variables qui ne sont pas associées à un objet un particulier lors de la définition de la fonction.
Lors de l’appel de la fonction les entrées sont définies et les inconnues seront associées au début de la fonction aux objets passés en entrée de la fonction.
fonction(4,6)
52
def autre_fonction(s : str, y : int) -> str : variable = s + ' - ' variable = variable * (y - 1) variable = variable + s return variable
autre_fonction("Nadine", 4)
Nadine - Nadine - Nadine - Nadine
2. Structures de Contrôle
Les instructions d’un programme sont exécutées dans l’ordre de leur
écriture, de haut en bas.
Les instructions sont organisées par blocs, c’est à dire pas ensemble
d’instructions succéssive dans le programme.
Les blocs d’instructions en Python sont séparés par des niveaux
d’indentation différents.
Les structures de contrôles permettent de modifier le flot d’exécution du programme.
2.1. Branchement conditionnel
if condition1 : # Block d'instruction 1 elif condition2 : # Block d'instruction 2 else : # Block d'instruction 3
si la condition1 est vrai alors
executer le block d'instruction 1
sinon si la condition2 est vrai alors
executer le block d'instruction 2
sinon (si la condition1 et la condition2 sont fausses)
executer le block d'instruction 3
Les blocs elif
et else
sont optionnels.
Les blocs elif
peuvent être repétés autant de fois que voulu.
Les conditions booléennes peuvent être : la valeur d’une variable
booléenne, le résultat d’une inégalité, la combinaison de conditions
plus simples à l’aide des connecteurs logiques and
, or
et not
.
Les objets booléens de base sont True
et False
. Les inégalités
renvoie l’une de ces deux valeurs :
1 < 2, 1 > 2, 1 <= 2, 1 >= 2, 1 == 2, 1 != 2
True | False | True | False | False | True |
2.2. Répétition
Il existe plusieurs types de boucles, mais toutes peuvent se ramener à la boucle tant que (while
) décrite ci-dessous.
while condition : # Block d'instruction ... #Suite du programme
tant que la condition est vrai
executer le block d'instructions
reprendre l'exécution ici quand la condition est fausse
3. Algorithme pour le calcul des termes d’une suite récurrente
On voit ici notre première technique algorithmique générique.
Soit une suite récurrente : \[ \begin{cases} u_0 & = a \\
u_{n+1} & = f(u_n) \end{cases} \] pour \(a\) et \(f\) fixés, où \(f\) est une
fonction définissant le calcul de \(u_{n+1}\) en fonction de \(u_n\).
L’algorithme qui prend en entrée \(n\) et qui calcul \(u_n\) procède comme
suit :
- On commence par \(u_0\)
- Si on a \(u_i\), on calcule \(u_{i+1}\) en faisant \(f(u_i)\)
- On recommence avec le rang suivant, et ce tant que \(i \neq n\)
Algorithme Calcul de \(u_n\)
Entrées : \(n\) un entier
Sortie : \(u_n\) le \(n^\text{ème}\) terme de la suite \(u\)
\(u \gets a\) \(~~~~\) # le terme courant calculé
\(i \gets 0\) \(~~~~\) # le rang courant calculé
tant que \(i \neq n\) faire
\(~~~~\) # calcul du terme suivant
\(~~~~\) \(u_\text{suivant} \gets f(u)\)
\(~~~~\) # le terme calculé devient le terme courant pour la prochaine étape de calcul
\(~~~~\) \(u \gets u_\text{suivant}\)
\(~~~~\) \(i \gets i + 1\)
fin tant que
renvoyer u
L’algorithme sert de base est peut être adapté pour traiter des variantes du problème initial :
- premier terme ou définition de la fonction en entrée
- recherche du rang \(n\) pour lequel \(u_n = x\) avec \(x\) en fixé ou en entrée
- calcul des termes d’une suite récurrente d’ordre 2, 3, …
- calcul des termes de suites adjacentes, mutuellement récurrentes, …
- …
Il faut parfois savoir faire apparaître une suite récurrente pour résoudre un problème qui ne semble initialement pas être le calcul d’une suite récurrente. Par exemple :
- calcul de \(n!\) : \((n+1)! = (n+1) \cdot n!\) , \(0! = 1\)
- calcul de la somme des éléments d’une suite : \(S_{n+1} = S_n + u_{n+1}\), \(S_0 = u_0\)
- calcul de la somme \(S\) des diviseurs de \(n\) :
- On pose \[S_i = \sum_{0 \leq d \leq i, ~~ n \, \% \, d \, = \, 0} d \]
- On a \(S = S_n\), \(S_0 = 0\) et \[S_{i+1} = \begin{cases} S_i & \text{ si } n \, \% \, (i+1) \, \neq \, 0 \\ S_i + (i + 1) & \text{ sinon} \end{cases}\]
4. Séquences
- Un conteneur est un objet python qui contient une collection d’autres objets python.
- Une sequence est un conteneur dont les éléments sont ordonnées.
Exemples de séquences :
Les châines de caractères sont des séquences de caractères
"Nadine"
Les tuples sont des séquences d’objets de taille fixe
(2,"Nadine",4.0)
Les listes sont des séquences d’objets de taille variabe >
['N',12,12,21,13.0,12]
- Les générateurs sont des séquences d’objets … particuliers ( et
hors programme ) !
range(0,n,1)
Exemples de conteneurs que l’on verra plus tard :
- Les ensembles
- Les dictionnaires
4.1. Opérations sur les séquences
La longueur d’une séquence s’obtient grâce à la fonction len
.
# Longueur len("Nadine"), len((1,2,3)), len([1,2,3,4,5]), len(range(12))
6 | 3 | 5 | 12 |
On peut créer une séquence vide. Sa longueur est 0.
"", (), [], range(0)
On accède à chaque élément d’une séquence grâce à son indice. Le
premier indice d’une séquence est 0 et le dernier est n-1 avec n
la taille de la séquence.
# Accès "Nadine"[0], (1,2,3)[2], [12.3,21.12,144.0][1], [0,0,0,1][2], range(2,4)[0], range(2,4)[1]
N | 3 | 21.12 | 0 | 2 | 3 |
Attention L’accès doit être fait sur un indice entre 0 et n-1
(1,2,3)[3]
--------------------------------------------------------------------------- IndexError Traceback (most recent call last) Input In [15], in <cell line: 1>() ----> 1 (1,2,3)[3] IndexError: tuple index out of range
La concatenation consiste à mettre deux séquence l’une à la suite de l’autre.
"Nadine" + "Cuisine", (4,2,3) + (4,5,6), [12,12,12] + [21,21,21]
NadineCuisine | (4 2 3 4 5 6) | (12 12 12 21 21 21) |
Attention : La concatenation ne fonctionne pas sur les générateurs.
range(2) + range(4)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Input In [17], in <cell line: 1>() ----> 1 range(2) + range(4) TypeError: unsupported operand type(s) for +: 'range' and 'range'
La multiplication par un entier permet de concatener plusieurs fois la chaîne avec elle même.
"Nadine"*3, (1,2) * 4, [0] * 12
NadineNadineNadine | (1 2 1 2 1 2 1 2) | (0 0 0 0 0 0 0 0 0 0 0 0) |
list("Nadine"), tuple("Nadine"), tuple([12,22,32]), list((12,22,32))
N | a | d | i | n | e |
N | a | d | i | n | e |
12 | 22 | 32 | |||
12 | 22 | 32 |
list(range(2,20,5))
2 | 7 | 12 | 17 |
str([1,2,5])
1 | 2 | 5 |
4.2. Parcours
Beaucoup d’algorithmes s’appuie sur un parcours de la séquence.
Un parcours consiste à réaliser un block d’instructions sur chaque
élément de la structure (ici la séquence).
L’ordre dans lequel sont visités les éléments peut varier selon le
parcours.
Pour les séquences il s’agit surtout de parcourir selon les indices
croissants (de \(0\) à \(n-1\)) ou décroissants (de \(n-1\) à \(0\)).
Les algorithmes de parcours de séquences se construisent avec une boucle tant que comme suit :
\(i \gets 0\)
\(n \gets \texttt{longueur}(S)\)
tant que i < n faire
\(~~~~\dots\) \(~~~~\) Instructions manipulants S[i]
\(~~~~\dots\) \(~~~~i \gets i+1\)
fin tant que
4.3. Caractères
Un caractère est représenté par l’ordinateur par un nombre. Il existe différente manière d’associer un caractère à un nombre. C’est ce qu’on appelle l’encodage.
Le codage ASCII permet de coder 255 caractères. Le codage utf-8 permet d’en coder \(2^{21}\) et donc de coder d’autres alphabets que l’alphabet latin.
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
---|---|---|---|---|---|---|---|---|---|---|
0 | ||||||||||
10 | ||||||||||
20 | ||||||||||
30 | ! | “ | # | $ | % | & | ’ | |||
40 | ( | ) | * | + | , | - | . | / | 0 | 1 |
50 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | : | ; |
60 | < | = | > | ? | @ | A | B | C | D | E |
70 | F | G | H | I | J | K | L | M | N | O |
80 | P | Q | R | S | T | U | V | W | X | Y |
90 | Z | [ | \ | ] | ^ | _ | ` | a | b | c |
100 | d | e | f | g | h | i | j | k | l | m |
110 | n | o | p | q | r | s | t | u | v | w |
120 | x | y | z | { | } | ~ |
On peut déclarer une chaîne de caractère :
- entre guillemets doubles (peut contenir des guillemets simples) :
"Nadine l'aristocrate"
- entre guillemets simples (peut contenir des guillemets doubles):
'Nadine le "vélociraptor"'
entre guillemets triples (peut contenir des retours à la ligne):
""" Nadine l'aristocrate Nadine le "vélociraptor" """
Si besoin on peut échapper le caractère "
pour l’insérer dans une
chaîne de caractère.
"Nad'i\"ne"
print("Nad'i\"ne")
Un saut de ligne est représenté par le caractère spécial '\n'
"Nadine" + "\n" + "Nadine"
print("Nadine" + "\n" + "Nadine")
En Jupyter-Python on peut obtenir le code ASCII d’un caractère ou obtenir le caractère à partir de son code ASCII :
ord('N'), ord('a'), ord('D'), chr(105), chr(78), chr(101), chr(9), chr(49), chr(50)
78 | 97 | 68 | i | N | e | \t | 1 | 2 |
5. Séquence - Outils
5.1. Séquence vide
On peut vérifier en Python si une séquence est vide grâce au mot clé not
.
not [], not (), not "", not range(0)
True | True | True | True |
not [12,12], not (1), not "Nadine", not range(12)
False | False | False | False |
5.2. Accès inverse
On peut accéder en Python à un élément en précisant son indice S[i]
. Si i
est un nombre négatif, l’accès se fait à partir de la fin de la liste, i.e si
\(S\) est une séquence de longueur \(n\) et \(k\) est un nombre positif :
\[S[-k] = S[n-k]\]
"Nadine"[-3], (1,2,3)[-1], [8,12,12,45,23,45][-6], range(12)[-1]
i | 3 | 8 | 11 |
Les indices valides pour l’accès en Python sur une séquence \(S\) de taille \(n\) sont donc les indices :
\[-n, -(n-1), \dots, 0, \dots n-1\]
5.3. Sous séquences, Séquences extraites et Slices
Une sous séquence d’une séquence \(S\) est une séquence \(T\) d’éléments consécutifs de \(S\).
Par exemple :
[12,67,89]
est une sous séquence de[1,4,7,12,67,89,67,90]
(1,2)
est une sous séquence de(1,2,3,4)
"din"
est une sous séquence de"Nadine"
- Une sous séquence d’une chaîne de caractères est appelée sous chaîne.
- La séquence vide et la séquence elle-même sont toujours des sous séquence d’une séquence.
Une séquence extraite d’une séquence \(S\) est une séquence \(T\) d’éléments de \(S\), ordonnés comme dans \(S\).
Par exemple :
[4,12,89,67]
est une séquence extraite de[1,4,7,12,67,89,67,90]
(1,4)
est une séquence extraite de(1,2,3,4)
"aie"
est une séquence extraite de"Nadine"
- Une sous séquence d’une séquence est une séquence extraite.
Les slices en Python permettent d’extraire une séquence depuis une séquence de base. Toutefois les extractions ne peuvent être faîtes que de manière régulières. Il est possible d’extraire une séquence en précisant :
- l’indice de départ de l’extraction,
i
, - l’indice de fin de l’extraction,
j
, - le pas qui sépare chaque éléments gardé dans l’extraction,
p
.
La syntaxe est la suivante :
S[i:j:p]
"NadineNadineNadine"[4:18:3], (1,2,3,4,5,6,7)[0:7:2]
nanan | (1 3 5 7) |
[12,12,12,21,21,21][2:4:2], range(12)[2:5:2]
(12) | range | (2 5 2) |
Voici quelques exemples intéressants :
"Nadine"[-1:-7:-1], (1,2,3,4,5,6,7,8,9)[7:1:-2]
enidaN | (8 6 4) |
[12,12,12,12][3:2:1], [12,21,12,21,12,21][2:-2:1]
12 | 21 |
A noter que :
- Dans \(S[i:j:p]\), \(S[i]\) est dans l’extraction, \(S[j]\) n’est pas dans l’extraction.
- \(S[i:i] = []\)
- \(S[i:j] = S[i:j:] = S[i:j:1]\)
- Si \(i\) est omis, il est considéré comme étant \(0\) :
- \(S[:j:p] = S[0:j:p]\), \(S[:j] = S[0:j] = S[0:j:1]\)
- Si \(j\) est omis, il est considéré comme étant \(n\) la taille de la séquence :
- \(S[i::p] = S[i:n:p]\), \(S[i:] = S[i:n] = S[0:n:1]\)
- On en déduit que \(S[:] = S[::] = S[0:n:1]\) est une copie de \(S\).
5.4. Appartenance
L’appartenance d’un élément à une séquence peut être testée par la condition suivante :
'c' in "Nadine", 4 in [12,12,4,4,6,4,9], 3 in (1,2,123), 12 in range(2,12,2)
False | True | False | False |
L’appartenance à une séquence vide est toujours fausse :
'a' in "", 0 in []
False | False |
Exemple :
def voyelle(c) : """ Entrées : un caractère c Sortie : le code ascii de c ssi c est une voyelle, -1 sinon """ code = -1 if c in "AEIOUYaeiouy" : code = ord(c) return code
voyelle('N'), voyelle('a'), voyelle('d')
-1 | 97 | -1 |
voyelle('i'), voyelle('n'), voyelle('e')
105 | -1 | 101 |
Le mot clé in
permet de tester si une chaîne de caractère est une sous
chaîne d’une autre. On dira que la sous chaîne appartient à l’autre
chaîne.
'ab' in "Nadine", "ab" in "Baobab"
False | True |
Attention cela ne fonctionne pas sur les autres séquences.
Exemple :
def nadine(s) : """ Entrées : une chaîne s Sortie : "Nadine" si "Nadine est dans s sinon "Cuisine" """ sortie = "Cuisine" if "Nadine" in s : sortie = "Nadine" return sortie
nadine("Baobab"), nadine("Nadine")
Cuisine | Nadine |
nadine("Nadine sous le Baobab"), nadine("Le Baobab sous Nadi...")
Nadine | Cuisine |
5.5. Parcours des éléments d’une séquence
5.5.1. Boucle pour
Python fourni une syntaxe particulière pour parcourir une séquence.
Il s’agit de la boucle for.
La syntaxe suivante permet de parcourir une séquence \(S\), dans l’ordre croissant d’indice. \(x\) est associé à chaque répétition à l’élément de \(S\) sur lequel doit être réalisé le bloc d’instructions.
pour chaque \(x \in S\) faire
\(~~~~\) # Block d’instruction sur \(x\)
\(~~~~\) …
\(\text{#}\) Suites de l’algorithme
for x in S : # Block d'instruction sur x ... ...
Attention le parcours vu avec une boucle tant que est un parcour des indices de la séquence. Ici la boucle pour fait un parcours des éléments, les indices de sont donc pas disponibles.
Le parcour d’indice peut aussi être fait grâce à une boucle for en utilisant le
générateur range(n)
.
n = len(S) for i in range(n) : # Block d'instruction sur S[i] ... ...
Le parcours d’indice est plus général que le parcours des éléments car il donne accès à la fois à un élément et à son indice.
5.5.2. Générateurs
Deux générateurs particuliers peuvent être créés depuis une séquence \(S\) de taille \(n\) :
enumerate(S)
génère les couples \((i,S[i])\), \(i\) allant de 0 à \(n-1\)reversed(S)
génère les éléments de \(S\) en ordre inverse.- Attention, tous les générateurs ne sont pas inversibles…
Les générateurs sont convertis en liste pour pouvoir être affichés.
list(enumerate("Nadine")), list(enumerate((8,9,0,3,-7)))
(0 N) | (1 a) | (2 d) | (3 i) | (4 n) | (5 e) |
(0 8) | (1 9) | (2 0) | (3 3) | (4 -7) |
list(enumerate([12]*4)), list(enumerate(range(4)))
(0 12) | (1 12) | (2 12) | (3 12) |
(0 0) | (1 1) | (2 2) | (3 3) |
list(reversed("Nadine")), list(reversed((8,9,0,3,-7)))
e | n | i | d | a | N |
-7 | 3 | 0 | 9 | 8 |
list(reversed([12,9,0,12,8])), list(reversed(range(12)))
8 | 12 | 0 | 9 | 12 | |||||||
11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
enumerate
permet de faire un parcour à la foi sur les indices et les éléments
sans avoir à utiliser l’accès.
for (i,x) in enumerate(S) : # Block d'instruction sur i et x ... ...
reversed
permet de faire un parcour des éléments par indices décroissants.
for x in reversed(S) : # Block d'instruction sur x ... ...
Un parcours sur les indices dans l’ordre décroissant se fait avec
range(n-1,-1,-1)
.
On peut combiner enumerate
et reversed
:
for (i,x) in enumerate(reversed(S)) : # Block d'instruction sur i et x ... ...
list(enumerate(reversed(range(2,100,8))))
0 | 98 |
1 | 90 |
2 | 82 |
3 | 74 |
4 | 66 |
5 | 58 |
6 | 50 |
7 | 42 |
8 | 34 |
9 | 26 |
10 | 18 |
11 | 10 |
12 | 2 |
Mais pas reversed
sur enumerate
…
reversed(enumerate("Nadine"))
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Input In [44], in <cell line: 1>() ----> 1 reversed(enumerate("Nadine")) TypeError: 'enumerate' object is not reversible
6. Mutabilité
6.1. Objets Mutables et Immutables
Les objets Python se séparent en deux catégories :
- les objets mutable peuvent être modifiés et évoluer dans l’exécution du programme.
- les objets immutables ne peuvent pas être modifiés et restent constant tout au long de l’exécution.
Mutables | Immutables |
---|---|
Listes | Entiers |
Dictionnaires | Flottants |
Ensembles | Booléens |
Chaînes de caractères | |
Tuples | |
6.2. Mutabilité des Listes
Les listes Python sont des objets mutables. On peut :
- modifier l’élément à un indice \(i\) donné :
l[i] = x
- ajouter un élément à la fin de la liste :
l.append(x)
supprimer un élément à la fin de la liste et le renvoyer :
x = l.pop()
L = [1,2,3] L[1] = 12 x = L.pop() L[0] = x L.append(144) L
3 12 144
Les opérations ci-dessus sont des requis du programme. Il en existe d’autres :
- ajouter un élément dans la liste à l’index \(i\) :
l.insert(i,x)
- supprimer un élément à l’indice \(i\) de la liste et le renvoyer :
l.pop(i)
modifier une sous-séquence de la liste :
l[i:j] = l1
L = [1,2,3,4,5] L.insert(1,12) x = L.pop(0) L[2:4] = [x*33,x*44,x*55,x*66] L
12 2 33 44 55 66 5
6.3. Mutabilité, variables et copies
La mutabilité concerne les objets et non pas les variables.
On peut modifier une variable associée à un objet immutable :
s = (1,2) s = (2,3) s
2 | 3 |
s = (1,2) s[0] = 2 s[1] = 3
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Input In [48], in <cell line: 2>() 1 s = (1,2) ----> 2 s[0] = 2 3 s[1] = 3 TypeError: 'tuple' object does not support item assignment
Attention quand deux variables sont associées au même objet mutable.
l1 = [1,2,3] l2 = [1,2,3] # l2 est associée à un objet différent de l1 l1[1] = 12 l1, l2
1 | 12 | 3 |
1 | 2 | 3 |
l1 = [1,2,3] l2 = l1 # l2 est associée au même objet que l1 l1[1] = 12 l1, l2
1 | 12 | 3 |
1 | 12 | 3 |
Attention en particulier au cas des fonctions.
def modif(l) : if not l : return None l[0] = 12 return None t = [1,2,3] modif(t) t
12 | 2 | 3 |
Attention à la copie de liste. De la même manière que l1 = l2
associe à l1
et l2
le même objet,
lors de la copie de liste l1[i] = l2[i]
associe à l1[i]
et l2[i]
les mêmes objets :
l1 = [ [1,1,1], [2,2,2], [3,3,3] ] l2 = [ None ] * 3 for i in range(3) : l2[i] = l1[i] l1,l2
(1 1 1) | (2 2 2) | (3 3 3) |
(1 1 1) | (2 2 2) | (3 3 3) |
l1[0][1] = 12 l1,l2
(1 12 1) | (2 2 2) | (3 3 3) |
(1 12 1) | (2 2 2) | (3 3 3) |
l1.append(l1[1])
l1
1 | 12 | 1 |
2 | 2 | 2 |
3 | 3 | 3 |
2 | 2 | 2 |
l1[1][2] = 144 l1
1 | 12 | 1 |
2 | 2 | 144 |
3 | 3 | 3 |
2 | 2 | 144 |
Le résultat est le même pour l2.append(l1[i])
, l2 = l2 + [ l1[i] ]
, l2 = [] + l1
, l2 = [ l1[i] for i in range(3) ]
,
l2 = l1[:]
, …
On retrouve ce même phénomène avec la multiplication :
l1 = [ [0,0,0] ] * 3 l1
0 | 0 | 0 |
0 | 0 | 0 |
0 | 0 | 0 |
l1[0][1] = 2 l1
0 | 2 | 0 |
0 | 2 | 0 |
0 | 2 | 0 |
7. Fichiers
Les fichiers sont accessible sur votre ordinateur grâce au chemin décrivant son emplacement dans l’arborescence de fichier. Le chemin décrit les dossiers à traverser pour atteindre le fichier.
Un chemin absolu, décrit l’ensemble des dossiers à traverser depuis la racine pour trouver le fichier. La racine est :
- Windows :
C:\\
, ou une autre lettre décrivant le disque surlequel vous êtes. - Linux et Mac :
/
Exemple :
C:\\Users\nadine\Documents\informatique\tp5\tp5.py
/home/nadine/informatique/tp5/tp5.py
Un chemin relatif, décrit l’ensemble des dossiers à traverser le dossier courant.
Exemple :
informatique\tp5\tp5.py
depuis le dossierDocuments
informatique/tp5/tp5.py
depuis le dossier utilisateurnadine
.
En Python on peut créer un objet fichier grâce à la fonction open
en
spécifiant son chemin et le mode d’ouverture :
file = open("informatique/tp5/tp5.py","r")
Mode d’ouverture | Description |
---|---|
r | Ouvre un fichier en lecture seule. Il est impossible de modifier le fichier. Le pointeur interne est placé au début du fichier. |
r+ | Ouvre un fichier en lecture et en écriture. Le pointeur interne est placé au début du fichier. |
a | Ouvre un fichier en écriture seule en conservant les données existantes. Le pointeur interne est placé en fin de fichier et les nouvelles données seront donc ajoutées à la fin. Si le fichier n’existe pas, le crée. |
a+ | Ouvre un fichier en lecture et en écriture en conservant les données existantes. Le pointeur interne est placé en fin de fichier et les nouvelles données seront donc ajoutées à la fin. Si le fichier n’existe pas, le crée. |
w | Ouvre un fichier en écriture seule. Si le fichier existe, les informations existantes seront supprimées. S’il n’existe pas, crée un fichier. |
w+ | Ouvre un fichier en lecture et en écriture. Si le fichier existe, les informations existantes seront supprimées. S’il n’existe pas, crée un fichier. |
L’objet fichier permet de manipuler le fichier dont le chemin a été donné en paramètre. Avec les droits adéquats :
- Lecture du fichier dans une chaîne de caractère :
s = file.read()
- Lecture du fichier dans une liste de chaînes de caractère. Chaque élément de
la liste correspond à une ligne du fichier :
l = file.readlines()
- Lecture d’une ligne du fichier dans une chaînes de caractère :
l = file.readline()
- Écriture d’une chaîne de caractère dans un fichier
n = file.write(s)
. La fonction renvoie le nombre de caractères écrits.
En plus de séparer le fichier en une liste de lignes, on peut le séparer en une
liste de mots. Un mot est un ensemble de caractère différent d’un espace. On
peut séparer les mots d’une chaîne de caractère pour obtenir une liste de mot
grâce à la fonction split
.
l = s.split()
L’objet fichier peut être itéré par une boucle pour, on récupère alors les lignes du fichier une par une.
for ligne in fichier : # Block d'instruction sur la ligne for i,ligne in enumerate(fichier) : # Block d'instruction sur la ligne et le numéro de ligne i.
Après utilisation (lecture, écriture ou les deux), le fichier doit être fermé :
file.close()
8. Compréhension de Liste
On peut créer une liste par un schéma de compréhension :
l = [ e for x in S ]
avec :
e
une expression Python quelconque en fonction des variables des boucles for.S
une séquence
Equivalents :
l = [] for x in S : l.append(x)
Exemple :
[ x * 2 + 1 for x in range(12) ]
1 | 3 | 5 | 7 | 9 | 11 | 13 | 15 | 17 | 19 | 21 | 23 |
On peut imbriquer deux boucles (ou plus) dans le schéma de compréhension :
[ x + y for x in range(4) for y in range(x) ]
1 | 2 | 3 | 3 | 4 | 5 |
La seconde boucle peut alors utiliser la variable de la première.
Equivalents :
l = [] for x in range(12) : for y in range(x) l.append(x + y)
On peut filtrer les éléments dans le schéma de compréhension :
[ x for x in range(21,42) if ( x*x ) % 3 == 1 ]
22 | 23 | 25 | 26 | 28 | 29 | 31 | 32 | 34 | 35 | 37 | 38 | 40 | 41 |
Equivalents :
l = [] for x in range(100) : if (x*x) % 3 == 1 : l.append(x)
9. Matrices ou Tableaux 2D
Une matrice, ou tableau deux dimensions, est une structure à deux dimensions où chaque éléments est accessible par un numéro de ligne est un numéro de colonne.
Exemple :
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | Nadine | Cuisine | Tajine | Mezzanine |
1 | 12 | 24 | 144 | 21 |
2 | Allan | Vélociraptor | Chuck | Berry |
3 | 0 | 0 | 0 | 0 |
4 | 12 | 12 | 12 | 12 |
5 | Do | Ré | Mi | Fa |
En Python une matrice est représentée par une liste de liste. Pour une matrice à \(n\) lignes et \(m\) colonne on utilisera une liste de taille \(n\) contenant des listes de taille \(m\).
Il faut voir une matrice comme la liste des lignes qui la compose.
9.1. Création d’une matrice de \(n\) lignes et \(m\) colonnes
def nouvelle_matrice(n,m) : """ Entrées : n et m deux entiers positifs Sortie : une matrice n x m initialement vide (remplie de None) """ t = [] for i in range(n) : t.append([ None ] * m ) return t
Une version courte pour la création de matrice avec un schéma de compréhension :
[ [ None ] * m for i in range(n) ]
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Input In [62], in <cell line: 1>() ----> 1 [ [ None ] * m for i in range(n) ] NameError: name 'n' is not defined
Attention le code ci-dessous ne crée pas une matrice valide. c.f la section Mutabilité, Variable et Copie
t = [ [ None ] * m ] * n
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Input In [63], in <cell line: 1>() ----> 1 t = [ [ None ] * m ] * n NameError: name 'm' is not defined
9.2. Accès
On peut accéder à l’élément à la ligne \(i\), colonne \(j\) de la matrice grâce à
l’instruction m[i][j]
, accès à l’élément d’indice j de la liste à l’indice i
de la matrice.
La ligne \(i\) de la matrice est accessible grâce à l’instruction m[i]
.
La colonne \(j\) est plus difficilement accessible. Il faut l’extraire de la matrice, par exemple avec le schéma de compréhension suivant :
# j fixé et n nombre de lignes col = [ t[i][j] for i in range(n) ]
Les dimensions de la matrices sont obtenue comme ceci :
- Nombre de ligne :
len(t)
. - Si le nombre de ligne n’est pas nul, Nombre de colonne :
len(t[0])
.
10. Modules
Un module est un fichier de code Python qui contient des fonctions et des objets Python implémentez par vous ou d’autre personnes. Pour pouvoir utiliser ces fonctions et objets, il faut importer le module.
On peut importer tout le module :
import nadine # Importe le module nadine import numpy as np # Importe le module numpy et le renomme np
Les fonctions et objets du modules sont alors accessible en précisant le nom, ou l’alias, du module avant la fonction :
x = nadine.fonction(y) t = np.full((12,12),0.0)
On peut importer une ou plusieurs fonctions ou objets particulier du module :
from nadine import fonction # Importe la fonction fonction du module nadine from numpy import full, array, matrix # Importe les fonctions full, array et matric du module numpy
On peut alors utiliser directement ces fonctions sans préciser le module :
x = fonction(y) t = full((12,12),0.0) t2 = array([1,2,3])
Une très mauvaise pratique consiste à importer toutes les fonction d’un module, sans importer le module en lui même :
from numpy import *
A éviter.