Aller au contenu

Algo & Prog 1 Python : CM séance 02

2. Structures de données

2.1. Tuples

On a vu les types simples de Python : int, float, bool, str
Il y a de nombreux autres types et opérations associées.

Un tuple = plusieurs valeurs séparées par une virgule

>>> a = 7, 4, "oui"       # (7, 4, "oui")

on peut mettre autour des parenthèses :

>>> a = (7, 4, "oui")     # (7, 4, "oui")
>>> type(a)               # <class 'tuple'>
>>> type(a) == tuple      # True

Comment récupérer les valeurs ?

>>> x, y, z = a
>>> x                     # 7
>>> y                     # 4
>>> z                     # "oui"

Si on se trompe ?

>>> x, y = a              # ValueError: too many values to unpack (expected 2)
>>> x, y, z, t = a        # ValueError: need more than 3 values to unpack

Utilité :

  • Échanger 2 variables x et y
    en C : tmp = x ; x = y ; y = tmp;
    en python : x, y = y, x

  • Une fonction peut renvoyer plusieurs valeurs :

    def saisir_nom_prenom_age () :
        nom = input("Nom = ")
        prenom = input("Prénom = ")
        age = int(input("Age = "))
        return nom, prenom, age
    
    n, p, a = saisir_nom_prenom_age()
    

Opérations sur un tuple :

  • concaténation

    >>> a = 7, 4, "oui"
    >>> b = True, 3.14
    >>> c = 5,
    >>> a+b                     # (7, 4, 'oui', True, 3.14)
    >>> a+c                     # (7, 4, 'oui', 5)
    
  • longueur

    >>> len(b)                  # 2
    
  • transtypage

    >>> tuple("salut")          # ('s', 'a', 'l', 'u', 't')
    
  • on peut aussi utiliser des indices, voir plus loin

2.2. Boucle for et range

Boucle for très différente du C :

for variable1 in expression1 :
    bloc1

expression1 est de n'importe quel type itérable ;
variable1 prend successivement les valeurs de chaque élément de expression1.

Exemples :

  • avec des tuples

    for i in 7, 4, "oui" :
        print(i)
    
  • avec des strings

    for c in "plop" :
        print(c)
    
  • avec une fonction qui renvoie un itérable :

    def quelques_entiers() :
        return 5, 7, 11
    for i in quelques_entiers() :
        print(i)
    

Comment faire une boucle sur un intervalle, comme en C ? Avec la classe range

for i in range(1,10) :
    print(i)              # de 1 à 10-1

On peut changer l'incrément :

for i in range(1,10,3) :
    print(i)              # 1 4 7

Usage :

  • boucler de 0 à b exclus dans le sens croissant : range (b)
  • boucler de a à b inclus dans le sens croissant : range (a, b+1)
  • boucler de b à a inclus dans le sens décroissant : range (b, a-1, -1)

Opérations supplémentaires sur un range

>>> a = range(5)
>>> type(a)              # <class 'range'>
>>> type(a) == range     # True
>>> tuple(a)             # (0, 1, 2, 3, 4)
>>> len(a)               # 5

2.3. Les listes

Une liste de valeurs entre crochets, éventuellement de types différents

>>> a = [7, 4, "oui"]
>>> type(a)             # <class 'list'>
>>> type(a) == list     # True

Accès au valeurs :

>>> a[0]                # 7
>>> a[3]                # IndexError : sortie de tableau
>>> a[-1]               # "oui" : dernier élément

Modification :

>>> a[1] = 3.14
>>> a                   # [7, 3.14, 'oui']
>>> a[3] = 5            # IndexError

Opérations :

  • longueur

    >>> len(a)              # 3
    
  • liste vide

    >>> len([])             # 0
    
  • concaténation

    >>> b = a + ['plop']    # [7, 4, 'oui', 'plop']         on crée b
    
  • apposition (append)

    >>> a += [123, 45]      # [7, 3.14, 'oui', 123, 45]     on modifie a
    >>> a.append(11)        # [7, 3.14, 'oui', 123, 45, 11]
    

Voir aussi : >>> help(list)

Itération :

for elt in a :
    print(elt)

Itérer en récupérant l'indice :

for i,elt in enumerate(a) :
    print(i,elt)

Transtypage :

>>> a = [7, 4, "oui"]

À la main :

def transformer_liste_en_tuple(maliste) :
    montuple = ()
    for elt in maliste :
        montuple += (elt,)
    return montuple

>>> transformer_liste_en_tuple(a)       # (7, 4, 'oui')

def transformer_tuple_en_liste(montuple) :
    maliste = []
    for elt in montuple :
        maliste += [elt]
    return maliste
    
>>> b                                   # (5, 7, 'oui')
>>> transformer_tuple_en_liste(b)       # [5, 7, 'oui']

>>> b = tuple(a)        # (7, 4, 'oui')
>>> c = list(b)         # [7, 4, 'oui']
>>> a == c              # True    (même longueur et valeurs)

Une fonction peut évidemment recevoir en paramètre une liste et renvoyer une liste. Exemple

def extraire_entiers_pairs (liste_entiers) :
    res = []
    for entier in liste_entiers :
        if entier % 2 == 0 :
            res += [entier]
    return res

>>> extraire_entiers_pairs ([5, 6, 8, 11])    # [6, 8]

Marche aussi sur les tuples, ou n'importe quel itérable :

>>> extraire_entiers_pairs ((5, 6, 8, 11))    # [6, 8] 
>>> extraire_entiers_pairs (range(5))         # [0, 2, 4]

Appartenance : in

>>> 4 in [7, 4, 'oui']      # True
>>> 4 in (7, 4, 'oui')      # True
>>> 4 in range(5)           # True
>>> 'lo' in 'plop'          # True

>>> [7,4] in [7, 4, "oui"]  # False : ne marche pas pour les sous-listes
>>> [5,7] in [[5,7],11]     # True

Obtenir l'indice :

>>> a.index(4)              # 1
>>> a.index(5)              # ValueError: 5 is not in list

Il faut tester d'abord avec in :

>>> valeur = 5
>>> if valeur in a :
        print(a.index(valeur))
    
>>> valeur = 4
>>> if valeur in a :
        print(a.index(valeur))

Bilan : différence entre tuples et les listes ?

  • On peut accéder aux tuples via un indice :

    >>> a = (5,7,11)
    >>> a[1]            # 7
    
  • On peut également éclater une liste :

    >>> b = [12, 15]
    >>> x,y = b         # x = 12 et y = 15
    

En fait, la différence est dans l'immuabilité.

2.4. Types immuables

Certains types sont immuables (uk : immutable) = on ne peut pas modifier une valeur.

Les types simples sont immuables :

  • int, float, bool, complex, NoneType : les valeurs sont des constantes

  • les str sont aussi immuables : on ne peut pas changer une lettre

    >>> s = "plop"
    >>> s[2]        # 'o'
    >>> s[2] = 'i'  # TypeError: 'str' object does not support item assignment
    
  • les tuples sont immuables :

    >>> a = (5,7,11)
    >>> a[1] = 13       # TypeError: tuple does not support item assignment
    

À l'inverse, les listes, dictionnaires et ensembles sont muables.

Intérêts :

  • une fonction ne peut pas modifier une variable immuable passée en paramètre (bien que passée par référence) ;
  • les types immuables sont hashables = peuvent servir de clé dans un dictionnaire (vu après).
Important :

Comme les variables sont des références, et que les listes sont muables, on a ceci :

>>> b = [7, 3]
>>> c = b                   # c et b désignent la même liste
>>> b.append(5)             # on modifie b
>>> c
[7, 3, 5]                   # on a donc aussi modifié c

De la même façon :

>>> def f(l) :              # passage de paramètre par référence
        l[0] = 9            # on modifie l
>>> f(b)
>>> b
[9, 3, 5]                   # la fonction a bien agi sur b

2.5. Dictionnaires

Un dictionnaire est une sorte de liste, qui associe des mots clés à des valeurs.

type : dict
syntaxe : { "mot clé" : valeur, ...}
dictionnaire vide : {}

Exemple : ingrédients de la pâte brisée

pate = { "farine" : "200g", 
         "beurre" : "100g", 
         "eau"    : "1 verre",
         "sel"    : "1 pincée" }
>>> pate
>>> type(pate)            # <class 'dict'>
>>> type(pate) == dict    # True

Retrouver une valeur

>>> pate["beurre"]    # '100g'

Gestion des erreurs

>>> pate["levure"]    # KeyError: 'levure'

On peut aussi utiliser get, ce qui évitera une erreur si non trouvé

>>> pate.get("beurre")    # '100g'
>>> pate.get("levure")    # None

Avec get on peut donner une valeur par défaut :

>>> pate.get("beurre", "pas prévu")     # '100g'
>>> pate.get("levure", "pas prévu")     # 'pas prévu'

On peut rajouter des clés ou modifier des valeurs :

>>> pate["farine"] = "250g"
>>> pate["sucre"]  = "3 cuillerées"
>>> pate        # {'sucre': '3 cuillerées', 'eau': '1 verre', 'beurre': '100g',
                # 'farine': '250g', 'sel': '1 pincée'}

L'ordre d'insertion est garanti depuis python 3.7, voir https://docs.python.org/3/whatsnew/3.7.html

On peut aussi concaténer avec un autre dictionnaire :

>>> pate.update( {"beurre"      : "125g", 
                  "jaune d'œuf" : 1, 
                  "cannelle"    : "1 cuillerée"} )
>>> pate    # {'cannelle': '1 cuillerée', 'beurre': '125g', "jaune d'œuf": 1, 
            #  'farine': '400g', 'sel': '1 pincée', 'eau': '1 verre'}

Si vous n'aimez pas la cannelle on peut la supprimer

>>> del pate["cannelle"]
>>> pate    # {'beurre': '125g', "jaune d'œuf": 1, 
            #  'farine': '400g', 'sel': '1 pincée', 'eau': '1 verre'}

Au fait combien faut-il d'ingrédients ? Demander la taille du dictionnaire :

>>> len(pate)    # 5

Test d'appartenance : clé in dictionnaire

>>> "farine" in pate    # True
>>> "bierre" in pate    # False

Je voudrais maintenant itérer sur les ingrédients, i.e. sur les clés :

>>> for ingredient in pate : 
        print(ingredient, pate[ingredient])

(pour python >= 3.7 on peut également constater ici que l'ordre d'insertion est préservé).

Variante : on récupère directement un tuple clé,valeur :

>>> for ingredient, quantite in pate.items() :
        print(ingredient, quantite)

C'est plus efficace.

On peut encore transformer les types :

>>> list(pate)          #  ['beurre', "jaune d'œuf", 'farine', 'sel', 'eau']
>>> tuple(pate)         #  ('beurre', "jaune d'œuf", 'farine', 'sel', 'eau')
>>> pate.keys()         #  dict_keys(['beurre', "jaune d'œuf", 'farine', 'sel', 'eau'])
>>> pate.values()       #  dict_values(['125g', 1, '400g', '1 pincée', '1 verre'])
>>> list(pate.values()) # ['125g', 1, '400g', '1 pincée', '1 verre']

Usage plus général :

  • on peut mettre autre chose que des strings en clé :

    >>> pipo = { 83 : "Var", 3.14 : "pi", True : "noir", (1,2,3) : "soleil" }
    >>> pipo[83]        # 'Var'
    >>> pipo[3.14]      # 'pi'
    >>> pipo[True]      # 'noir'
    >>> pipo[(1,2,3)]   # 'soleil'
    

    On ne peut mettre que des types immuables (car ils sont hashables)
    → des int, floal, bool, None, tuple et str ; pas list ni dict.

  • on peut mettre n'importe quel type en valeur :

    >>> foot = { "champions" : (1998, 2018),
                 "pays"      : { 1986 : "Mexique",
                                 1998 : "France", 
                                 2018 : "Russie" },
                 10          : ["Platini", "Zidane", "Mbappé"],
               }
    >>> foot["champions"]    # (1998, 2018)
    >>> foot["pays"]         # {1986: 'Mexique', 2018: 'Russie', 1998: 'France'}
    >>> foot["pays"][1998]   # 'France'
    >>> foot["10"]           # KeyError: '10'
    >>> foot[10]             # ['Platini', 'Zidane', 'Mbappé']
    >>> foot[10][1]          # 'Zidane'
    

Conclusion :

  • outil très puissant, permet de structurer les informations ;
  • par prudence : utiliser un seul type pour les clés, et un seul pour les valeurs.

2.6. Ensembles

Un ensemble est une collection de valeurs uniques.

Syntaxe :

>>> s = set()
>>> s.add(5)
>>> s.add(7)
>>> s.add(5)
>>> s                   # { 5, 7}
>>> type(s)             # <class 'set'>
>>> type(s) == set      # True
>>> t = {5, 7}
>>> s == t              # True

Pour rendre uniques une liste de valeurs il suffit de la convertir en set :

>>> l = []
>>> for i in range(10) :
        l.append(i*i % 7)
>>> l
[0, 1, 4, 2, 2, 4, 1, 0, 1, 4]
>>> s = set(l)
>>> s
{0, 1, 2, 4}
>>> t = list(s)
>>> t
[0, 1, 2, 4]

Autre exemple :

>>> melodie = ['do','mi','sol','mi','fa','mi','do']
>>> notes = set(melodie)          # {'sol', 'fa', 'do', 'mi'}

Les set sont itérables et supportent le test d'appartenance in :

>>> for note in notes :
        print(note)                     # sol fa do mi
>>> 'mi' in notes                       # True

Suppression d'un élément :

>>> notes.discard('mi')                 # {'sol', 'fa', 'do'}
>>> notes.discard('ré')                 # sans effet

Il y a de nombreuses autres méthodes : intersection, union, difference, ...

Voir help(set) pour approfondir.