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

Le branchement conditionnel permet de sauter par dessus des blocs d'instructions selon le résultat d'une condition booléenne.
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

La boucle permet de répeter un bloc d'instructions tant que le résultat d'une condition booléenne est vrai .

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 :

  1. On commence par \(u_0\)
  2. Si on a \(u_i\), on calcule \(u_{i+1}\) en faisant \(f(u_i)\)
  3. 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 dossier Documents
  • informatique/tp5/tp5.py depuis le dossier utilisateur nadine.

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 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 :

  1. Nombre de ligne : len(t).
  2. 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.

Author: Samy Jaziri

Created: 2022-12-01 Thu 14:05