NSI 1ère

Types structurés

Introduction

On peut séparer les types de variable en deux groupes :
  • les types 'atomiques" comme les entiers  int , les flottants  float , les booléens  bool 
  • les types dits structurés, ou construits , dont on a rencontré deux exemples jusqu'à présent : le type chaîne de caractère  str  et le  range  (utilisé notamment, mais pas seulement, dans les boucles bornées).

    Les variables de type structuré sont formées d'un certain nombre d'éléments auxquels on peut accéder un à un : une chaîne de caractère est composée de caractères, un  range  est composé de valeurs numériques.

    La fonction  len()  appliquée à un type structuré renvoie son nombre d'éléments.
    Nous l'avons utilisée avec les chaînes de caractères mais on peut l'appliquer aussi aux autres types structurés, par exemple aux range  , même si cela présente peu d'intérêt dans ce cas précis.

    >>> len(range(10,21))
    11

    De même nous avons utilisé les opérateurs  in  ou  not ..in  aussi bien avec les  str  qu'avec les  range . Ces opérateurs qui permettent de vérifier la présence d'un élément, ou de parcourir une variable structurée au cours d'une boucle (  for...in  ) s'appliqueront aussi aux autres types structurés.
    Les fonctions  min()  et  max()  s'appliquent aussi à beaucoup de types structurés et renvoient respectivement le plus petit et le plus grand de leurs éléments. Dans le cas d'une chaîne de caractère les caractères sont comparés par ordre de point de code unicode.

    Accès par indice(types ordonnés)

      str   et  range  font partie des types structurés ordonnés : leurs éléments sont rangés dans un ordre précis. A ce titre on peut accéder à l'un des éléments à l'aide de son indice, c'est à dire son n° de position. En Python comme dans beaucoup de langages les indices commencent à la valeur 0.
    >>> mot='Bonjour'
    >>>mot[0]
    'B'
    >>> mot[2]
    'n'

    >>> gamme=range(12,26)
    >>> gamme[0]
    12
    >>> gamme[3]
    15
    >>> gamme[len(gamme)-1]
    25

    Une distinction importante entre les différents types structurés est le fait que certains soient modifiables (muables, mutable en anglais) et d'autres non (immuables, immutable en anglais).

      str  et range   font partie des types non modifiables, ou immuables : on peut consulter la valeur de leurs éléments, soit en les parcourant avec  for ...in   soit en accédant aux élément par leur indice, mais on ne peut pas modifier les éléments

    Listes Python

    Le type   list   de Python est un type structuré ordonné et modifiable. Les éléments d'une liste peuvent être de n'importe quel type. ( Ils peuvent même ne pas être tous du même type, bien que cela soit rarement utile en pratique. En NSI on se limitera aux listes dont les éléments sont de même type).
    Une liste Python est délimitée par des crochets [ ], et ses éléments sont séparés par des virgules.

    On peut appliquer à une liste les opérateurs  in et not in  ainsi que les fonctions len, min   et   max .

    Création d'une liste

    On peut créer une liste vide [] ou une liste contenant des éléments, par exemple ['a','c','b'].

    Création et affichage d'une liste vide:

    >>> ma_liste=[]
    >>> print(ma_liste)
    []

    Création d'une liste contenant les entiers 1,2,9,8 :

    >>> ma_liste=[1,2,9,8]
    >>> print(ma_liste)
    [1, 2, 9, 8]

    Il est également possible de créer une liste à partir d'une autre variable structurée, à l'aide de la fonction  list() .

    A partir d'une chaîne de caractères :

    >>> ma_chaine="mercredi"
    >>> ma_liste=list(ma_chaine)
    >>> print(ma_liste)
    ['m', 'e', 'r', 'c', 'r', 'e', 'd', 'i']

    A partir d'un   range   :

    >>> ma_liste=list(range(4,20,2))
    >>> ma_liste
    [4, 6, 8, 10, 12, 14, 16, 18]

    On peut aussi construire une liste contenant n fois la même valeur.

    >>>liste=[1]*10
    >>>liste
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

    Accès aux éléments d'une liste

    Les éléments d'une liste peuvent être accédés par leur indice, que ce soit en consultation (pour connaître leur valeur) ou en modification (pour changer leur valeur).

    Affichage de l'élément d'indice 1 (donc le deuxième à partir du début de la liste) :

    >>> print(ma_liste[1])
    2
    Modification de la valeur de l'élément d'indice 2 (donc le troisième à partir du début): ici on remplace donc la valeur 9 par la valeur 4 dans la liste ma_liste.
    >>> ma_liste[2]=4
    >>> print(ma_liste)
    [1, 2, 4, 8]

    Parcours d'une liste

    Il est très fréquent d'avoir à parcourir la totalité des éléments d'une liste.

    Comme pour une chaîne de caractères ou un range , on peut parcourir une liste en utilisant for...in . Cette méthode permet cependant seulement de consulter les valeurs de éléments de la liste, mais non de les modifier (parcours en consultation seulement).

    Pour pouvoir parcourir une liste tout en ayant la possibilité de modifier les valeurs de ses éléments, on doit accéder aux éléments par leur indice, à l'aide d'une boucle  for... , comme ci-dessous:

    1. ma_liste=list(range(0,40,3))
    2. print(ma_liste)
    3. for i in range(0,len(ma_liste)):
    4.     ma_liste[i]=ma_liste[i]+2
    5. print(ma_liste)

    Les méthodes de liste

    Les listes supportent l'utilisation de fonctions particulières, qu'on appelle des méthodes.
    La méthode  append()  permet en particulier d'ajouter un élément à la fin de la liste.
    Sa syntaxe d'appel est nom_de_la_liste.append(élément_à_ajouter)

    Par exemple :

    >>> ma_liste=[1,5,7]
    >>> ma_liste.append(9) #permet d'ajouter la valeur 9 à la fin de la liste
    >>> print(ma_liste)

    L'appel d'une méthode sur une liste se fait toujours de la même façon: on fait suivre le nom de la liste d'un point, puis du nom de la méthode, en indiquant entre parenthèses les paramètres éventuels.

    La méthode clear()   permet de supprimer tous les éléments d'une liste, ou d'un autre type structuré modifiable.

    >>> ma_liste.clear()
    >>> ma_liste
    []

    La méthode insert(i,x)  permet d'insérer l'élément x à l'indice i dans la liste.

    La méthode  pop(i)  permet de retirer l'élement d'indice i. Si i n'est pas indiqué c'est le dernier élément qui est enlevé. La méthode pop retourne l'objet enlevé.

    La méthode remove(x)  permet de retirer l'élément x, s'il existe.

    La liste complète des méthodes de liste avec leur description peut être trouvée ici

    Remarque : étant donné que les chaînes de caractères ne sont pas modifiables alors que les listes le sont, il peut être pratique de passer par une liste quand on veut modifier certains caractères dans une chaîne. On utilise alors list( chaine )  pour obtenir une liste des caractères de la chaîne puis, après modification,  ''.join( liste )  pour réobtenir une chaîne de caractères à partir de la liste.

    Exemple :

    >>> ma_chaine='séquence'
    >>> ma_liste=list(ma_chaine)
    >>> ma_liste[4]='i'#change le premier 'e' en 'i'
    >>> ma_liste[1]='e'#change le 'é' (indice 1) en 'e'
    >>> ma_liste.remove('c')#enlève la lettre 'c'
    >>> ma_liste.pop()#enlève la dernière lettre
    'e' # la dernière lettre était un e
    >>>ma_liste.append('s')#ajoute un 's' à la fin
    >>> ma_liste
    ['s', 'e', 'q', 'u', 'i', 'n', 's'] #la liste obtenue après ces modifications
    >>> ma_chaine=''.join(ma_liste) #concaténe tous les caractères contenus dans la liste, pour obtenir une chaîne de caractères.
    >>>ma_chaine
    'sequins'

    Divers

    Deux listes peuvent être fusionnées par addition (alternative à la méthode  extend ):

    >>> liste1=['A','E','I']
    >>> liste2=['x','y','z']
    >>> liste1=liste1+liste2
    >>> print(liste1)
    ['A', 'E', 'I', 'x', 'y', 'z']

    On peut supprimer un élément d'une liste en utilisant le mot-clé del  (alternative à l'utilisation de la méthode  pop(i) 

    >>> ma_liste=['A','E','V','I','O','U','Y']
    >>> del ma_liste[2]
    >>> ma_liste
    ['A','E','I','O','U','Y']

    On peut mélanger aléatoirement les éléments d'une liste en utilisant  random.shuffle()  :

    1. import random
    2. ma_liste=[]
    3. for i in range(1,20):
    4.     ma_liste.append(i)
    5. print("liste initiale : \n",ma_liste)
    6. random.shuffle(ma_liste)
    7. print("liste mélangée aléatoirement: \n",ma_liste)
    Réciproquement, on peut obtenir une copie triée d'une liste en utilisant la fonction sorted()  (alors que la méthode de liste sort  trie la liste en place)
    >>>copie_triee=sorted(ma_liste)

    On peut éliminer les doublons d'une liste en utilisant la combinaison de fonctions list(set()) 

    >>>ma_liste=[4,2,6,8,7,2,-1,4,12,7,2,15]
    >>>ma_liste=list(set(ma_liste))
    >>>ma_liste
    [2, 4, 6, 7, 8, 12, 15,-1]
    Remarque : cette technique ne conserve pas l'ordre initial des éléments dans la liste car elle passe par l'intermédiaire du type structuré non ordonné set (ensemble)

    Tuples

    Un   tuple   est un type ordonné, comme le type  list  , mais il est non modifiable (immuable) . On crée un tuple en écrivant une suite de valeurs, séparées par des virgules. Pour plus de clarté, on écrit souvent le  tuple   entre parenthèses.

    >>>tu=1,3,5,12
    >>>tu
    (1,3,5,12)

    Puisque le tuple est ordonné, on peut accéder à chaque élément par son indice.

    >>>tu[1]
    3

    Comme les listes, les tuples peuvent contenir des éléments de n'importe quel type, et même de types différents.

    On peut appliquer aux tuples, comme aux listes,   in,not in, len, min   et   max . Un tuple peut donc être parcouru par for...in 

      Exemples d'applications pratiques des tuples en Python :
    • Affecter plusieurs variables en une seule ligne de code :

      >>>x,y=2,5
      >>>x
      2
      >>>y
      5
      >>>x,y
      (2,5)
      L'instruction x,y=2,5  a donc créé à la fois une variable x valant 2 et une variable y valant 5.

    • Echanger les valeurs de deux variables, en une seule ligne et sans avoir à passer par une variable auxiliaire:

      >>>a=16.5
      >>>b=49.3
      >>>a,b=b,a
      >>>a
      49.3
      >>>b
      16.5

    • Renvoyer plusieurs données en retour d'une fonction, sous la forme d'un tuple.

      1. def div_entiere(x,y):
      2.     q=x//y
      3.     r=x%y
      4.     return q,r
      5. resultat=div_entiere(149,16) #récupération du résultat de l'appel de la fonction dans la variable resultat
      6. print(resultat)# on peut constater que cette variable est un tuple
      7. print("quotient",resultat[0])# affichage du premier élément du tuple, qui contient le quotient
      8. print("reste",resultat[1])# affichage du deuxième élément du tuple, qui contient le reste
      9. # autre méthode :
      10. quotient,reste=div_entiere(283,26)#récupération explicite du résultat de l'appel dans un tuple
      11. print("quotient",quotient)
      12. print("reste",reste)

    Dictionnaires

    Un dictionnaire Python est un type structuré modifiable et non ordonné. Il s'agit d'un ensemble de paires clé-valeur . L'accès à un élément se fait à l'aide de sa clé .

    Un dictionnaire est délimité par des accolades { } et les paires clé-valeur sont séparées par des virgules.

    Une clé est usuellement une valeur numérique (entière de préférence) ou une chaîne de caractères, mais elle peut aussi être de n'importe quel autre type non modifiable (ainsi un tuple pourrait servir de clé, mais pas une liste)

    Une clé de dictionnaire doit être unique : une même valeur de clé ne peut être utilisée pour deux paires clés-valeurs d'un même dictionnaire.

    Création d'un dictionnaire

    Création d'un dictionnaire vide :

    >>>mon_dico={}

    Création d'un dictionnaire contenant quelques paires clé-valeur:

    >>>anniversaires={'Jules':'12 Mars','Elise' : '25 Juin','Younes' : '15 Octobre'}

    Dans ce dictionnaire, les prénoms sont les clés, et les dates sont les valeurs. On pourrait imaginer par exemple que ce dictionnaire sert à mémoriser et retrouver des dates d'anniversaire.

    Un dictionnaire peut aussi être créé en appliquant la fonction dict()  à une liste de tuples à deux éléments donnés dans l'ordre (clé,valeur) :

    >>>liste=[('Jules','12 Mars'),('Elise','25 Juin'),('Younes','15 Octobre')]
    >>>anniversaires=dict(liste)
    à faire plutôt en une seule ligne, à condition de faire attention aux parenthèses/crochets:
    >>>anniversaires=dict([('Jules','12 Mars'),('Elise','25 Juin'),('Younes','15 Octobre')])

    Enfin quand comme ici les clés sont des chaînes de caractères simples (sans espaces ni ponctuation) , on peut aussi créer le dictionnaire en utilisant la fonction dict()  avec la syntaxe suivante:

    >>> anniversaires=dict(Jules='12 Mars',Elise='25 Juin',Younes='15 Octobre')
    l'intérêt étant de se passer de guillemets pour écrire les clés (attention,les guillemets restent obligatoires pour les valeurs si ce sont comme ici des chaînes de caractères)

    Accès aux valeurs

    Il existe deux façons d'accéder à une valeur, toujours en utilisant la clé qui lui est associée:

    Première façon:

    >>>anniversaires['Jules']
    '12 Mars'
    Si la clé n'existe pas, une erreur de clé (KeyError)se produira .

    Deuxième façon, en utilisant la méthode get():

    >>>anniversaires.get('Elise')
    '25 Juin'
    >>>anniversaires.get('Anna')

    >>>anniversaires.get('Anna','clé absente')
    'clé absente'
    L'utilisation de la méhode 'get' ne provoque jamais d'erreurs même si la clé n'est pas trouvée.

    Les opérateurs in  et not in  s'appliquent aux clés pour un dictionnaires. For...in   permet de parcourir le dictionnaire clé par clé.

    1. for c in anniversaires:
    2.     print(c,'\t:\t',anniversaires[c])

    Appliquée à un dictionnaire, la fonction list()  renvoie une liste contenant toutes les clés du dictionnaire

    Ajout d'éléments

    Ajout d'un couple clé-valeur

    >>>anniversaires['Alicia']='9 Février'
    >>>anniversaires
    {'Jules': '12 Mars', 'Elise': '25 Juin', 'Younes': '15 Octobre','Alicia':'9 Février'}

    Si on entre un couple clé-valeur en utilisant une clé déjà présente dans le dictionnaire, la valeur précédente est écrasée.

    Retrait d'éléments

    Pour retirer un élément du dictionnaire, on peut utiliser  del  ou bien la méthode  pop(clé) , qui retourne la valeur associée à la clé.
    >>>del anniversaires['Jules']
    >>>anniversaires
    {'Elise': '25 Juin', 'Younes': '15 Octobre','Alicia':'9 Février'}
    >>>jour=anniversaires.pop('Elise')
    >>>jour
    '25 Juin'
    >>>anniversaires
    {'Younes': '15 Octobre','Alicia':'9 Février'}

     del  et pop()  provoqueront tous deux une erreur de clé (KeyError) s'ils la clé fournie ne figure pas dans le dictionnaire. On peut empêcher la survenue d'erreur en utilisant pour la méthode pop un second paramètre à retourner si la clé fournie n'existe pas.

    Par exemple  anniversaires.pop('Miriam')  provoquera une erreur car 'Miriam' n'est pas une clé du dictionnaire anniversaire, alors que  anniversaires.pop('Miriam','pas trouvé')  renverra 'pas trouvé', sans provoquer d'erreur (même principe que pour get() )

    Vues synchronisées d'un dictionnaire

    Les vues de dictionnaire sont un moyen commode de consulter le contenu d'un dictionnaire.
      Une vue d'un dictionnaire peut être obtenue à l'aide d'une des méthodes suivantes :
    •  items()  renvoie une vue de toutes les paires clé-valeur.
    •  values()  renvoie une vue de toutes les valeurs.
    •  keys()  renvoie une vue de toutes les clés.
    >>> anniversaires={'Jules':'12 Mars','Elise' : '25 Juin','Younes' : '15 Octobre'}
    >>> paires=anniversaires.items()
    >>> cles=anniversaires.keys()
    >>> valeurs=anniversaires.values()
    >>> paires
    dict_items([('Jules', '12 Mars'), ('Elise', '25 Juin'), ('Younes', '15 Octobre')])
    >>> cles
    dict_keys(['Jules', 'Elise', 'Younes'])
    >>> valeurs
    dict_values(['12 Mars', '25 Juin', '15 Octobre'])

    Une vue est un type structuré auquel on peut appliquer in  et not...in , et qui peut être parcouru par for...in 

    >>>'Elise' in cles
    True
    >>> for v in valeurs:
    ...        print(v)
    ...
    ...
    12 Mars
    25 Juin
    15 Octobre

    Les éléments de la vue items sont des tuples :

    >>>anniversaires={'Jules':'12 Mars','Elise' : '25 Juin','Younes' : '15 Octobre'}
    >>>for (nom,jour) in anniversaires.items():
    ...    print("L'anniversaire de",nom,"est le",jour)
    ...
    ...
    L'anniversaire de Jules est le 12 Mars
    L'anniversaire de Elise est le 25 Juin
    L'anniversaire de Younes est le 15 Octobre

    Les vues de dictionnaire sont synchronisées avec le dictionnaire :

    >>> anniversaires={'Jules':'12 Mars','Elise' : '25 Juin','Younes' : '15 Octobre'}
    >>> cles=anniversaires.keys()
    >>> cles
    dict_keys(['Jules', 'Elise', 'Younes'])
    >>> anniversaires['Alicia']='9 Février'
    >>> cles
    dict_keys(['Jules', 'Elise', 'Younes', 'Alicia'])
    Dans l'exemple ci-dessus, une vue des clés du dictionnaire est affectée à la variable cles, puis le dictionnaire est modifié (ajout d'un élément). Après modification, on constate que le contenu de cle a changé : il a été mis à jour en fonction de la modification du dictionnaire.

    A contrario, une liste des clés obtenue en appliquant la fonction list  au dictionnaire n'est, elle, pas actualisée.

    >>> anniversaires={'Jules':'12 Mars','Elise' : '25 Juin','Younes' : '15 Octobre'}
    >>> cles=anniversaires.keys()
    >>> liste_cles=list(anniversaires)
    >>> cles
    dict_keys(['Jules', 'Elise', 'Younes'])
    >>> liste_cles
    ['Jules', 'Elise', 'Younes']
    >>> anniversaires['Alicia']='9 Février'
    >>> cles
    dict_keys(['Jules', 'Elise', 'Younes', 'Alicia'])
    >>> liste_cles
    ['Jules', 'Elise', 'Younes']

    Approfondissements

    Listes multidimensionnelles

    Une liste peut contenir n'importe quel type d'élément, en particulier des listes.

    Une liste contenant plusieurs listes de même taille est l'équivalent d'une matrice, ou d'une grille de tableau.

    Par exemple :

    >>>liste=[['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]

    Pour accéder à un élément de cette liste, on doit indiquer deux indices : le premier est l'indice de la sous-liste contenant l'élément recherché, et le deuxième l'indice de l'élément dans la sous-liste.

    >>>liste[0][2]
    'c'
    >>>liste[2][0]
    'g'

    Définitions en compréhension

    Il est fréquent d'avoir à remplir une liste avec un nombre défini d'éléments remplissant une condition donnée.

    Supposons par exemple qu'on veuille construire une liste contenant les 10 premiers multiples de 3. On peut faire :

    1. multiples3=[]
    2. for i in range(10):
    3.     multiples3.append(3*i)

    Il est possible d'obtenir le même résultat en une seule ligne :

    1. multiples3=[3*i for i in range(10)]
    On dit alors qu'on a défini la liste en compréhension.

    Il est également possible d'ajouter une condition à vérifier par les éléments. Par exemple si on souhaite construire une liste des multiples de 3 inférieurs à 60 et qui ne soient pas des multiples de 4, on pourrait faire, classiquement :

    1. multiples3n4=[]
    2. for i in range(20):
    3.     if (3*i%4!=0):
    4.         multiples3n4.append(3*i)

    En compréhension de liste, cela devient:

    1. multiples3n4=[3*i for i in range(20) if 3*i%4!=0]

    On peut aussi obtenir la liste multiples3n4 à partir de la liste multiples3 créé précédemment, à l'aide de la définition en compréhension suivante :

    1. multiples3n4=[n for n in multiples3 if n%4!=0]
    Une compréhension de liste doit indiquer au moins la forme des éléments à placer dans la liste et une clause for..in  qui permet de déterminer le nombre d'éléments de la liste. Elle peut comporter également une condition.

    Copie et comparaison de variables structurées

    Recopier la valeur d'une variable d'un type atomique comme int, float ... dans une autre variable revient à faire une affectation à l'aide d'un signe =. Lors de cette opération, un nouvel espace est réservé en mémoire pour la nouvelle variable créée, et la valeur de l'ancienne variable est recopiée dans cet espace. A partir de là, il n'existe plus de lien entre ces deux variables, en dehors d'une égalité de valeurs temporaire.

    >>>ancienne=4156
    >>>nouvelle=ancienne
    >>>ancienne
    4156
    >>>nouvelle
    4156
    >>>nouvelle=6521
    >>>ancienne
    4156
    >>>nouvelle
    6521

    Il n'en va pas de même pour les listes et les dictionnaires.

    >>> ancienne=[1,2,3,4,5]
    >>> nouvelle=ancienne
    >>> nouvelle
    [1, 2, 3, 4, 5]
    >>> ancienne
    [1, 2, 3, 4, 5]
    >>> nouvelle[2]='surprise!'
    >>> nouvelle
    [1, 2, 'surprise!', 4, 5]
    >>> ancienne
    [1, 2, 'surprise!', 4, 5]
    Avec cet exemple, on constate que lorsque la 'nouvelle' liste est modifiée, l''ancienne' l'est également.

    L'explication en est que la ligne nouvelle=ancienne  ne crée pas une nouvelle liste, mais simplement un nouveau nom pour la même liste, désignant le même emplacement en mémoire.

    Il en va de même pour les dictionnaires.

    Pour créer effectivement une nouvelle liste (ou un nouveau dictionnaire) copie d'une précédente, il faut utiliser la méthode copy() 

    >>> ancienne=[1,2,3,4,5]
    >>> nouvelle=ancienne.copy()
    >>> ancienne
    [1, 2, 3, 4, 5]
    >>> nouvelle
    [1, 2, 3, 4, 5]
    >>> nouvelle[2]='surprise!'
    >>> nouvelle
    [1, 2, 'surprise!', 4, 5]
    >>> ancienne
    [1, 2, 3, 4, 5]

    Il est possible de comparer des listes, et en particulier de vérifier si elles sont égales (c'est vrai si tous les éléments sont identiques et dans le même ordre).
    Pour vérifier si deux noms de liste désignent en fait le même emplacement mémoire (identité des deux listes) , on utilise l'opérateur is  :

    >>> liste1=[4,5,6]
    >>> liste2=liste1
    >>> liste3=liste1.copy()
    >>> liste1==liste2
    True
    >>> liste1==liste3
    True
    >>> liste1 is liste2
    True
    >>> liste1 is liste3
    False

    Dans cette exemple, les trois listes liste1,liste2,liste3 ont exactement le même contenu et donc le test d'égalité entre elle (avec  == ) renvoie True. Mais le test d'identité (avec is  ) ne donne True qu'entre liste1 et liste2, qui désigne les mêmes données en mémoire.