Licence CC BY-NC-ND Thierry Parmentelat
_images/inria-25-alpha.png

types évolués

la liste

permet de créer des collections très souples :

  • séquence d’objets de n’importe quel type

  • on peut insérer / détruire des objets

  • pas de contrainte sur la taille

# on crée une liste avec des [ ]
homogene = [0, 12]
homogene
[0, 12]
# on peut mélanger
# les types
heterogene = [2.3, "abc"]
heterogene
[2.3, 'abc']
# des listes dans des listes
groupe = [True, homogene,
          "chaine", heterogene]
groupe
[True, [0, 12], 'chaine', [2.3, 'abc']]
type(groupe)
list
groupe
[True, [0, 12], 'chaine', [2.3, 'abc']]
# comme avec les chaines
# on peut accéder au i-ème élément
# les indices commencent à 0

# le premier élément est
# donc le booléen
groupe[0]
True
# on peut remplacer un élément
groupe[1] = '-'
groupe
[True, '-', 'chaine', [2.3, 'abc']]
# et le dernier
groupe[-1]
[2.3, 'abc']
# est heterogene
groupe[-1] == heterogene
True
# le slicing s'applique aussi
# comme sur les chaines de caractère
groupe[::2] # du début à la fin avec un pas de 2
[True, 'chaine']

liste et opérateurs

de nombreux opérateurs sont définis aussi sur les listes

# on peut ajouter deux listes,
# ça les concatène
[1, 2, 3] + [4, 5, 6]
[1, 2, 3, 4, 5, 6]
# la comparaison est
# lexicographique

[1, 2, 3] <= [1, 2, 4]
True
# l'opérateur d'appartenance
'chaine' in groupe
True
# et sa négation
'tutu' not in groupe
True

itérations

approfondi dans une section ultérieure
mais dans sa forme la plus simple: for .. in .. :

for item in groupe:
    print(item)
True
-
chaine
[2.3, 'abc']

listes et performances

À savoir
la liste est une structure de données très souple du coup elle n’est que relativement efficace
elle est surtout optimisée pour être modifiée par la fin
habituellement à base des méthodes append et pop

tutu = []

# on n'a pas encore vu le for
# mais vous pouvez deviner ce que ça fait
for c in 'abc':
    tutu.append(c)
    print(tutu)
['a']
['a', 'b']
['a', 'b', 'c']
# et à l'envers
while tutu:
    c = tutu.pop()
    print(c)
c
b
a

MAIS cela n’est un problème qu’avec des données nombreuses - $10^4$
du coup pour des preuves de concept la liste est TRÈS flexible et pratique

le tuple

similaire à la liste, mais qu’on ne peut pas modifier
ne sera pas approfondi dans ce primer
on va voir tout de suite à quoi ça peut bien servir

# ressemble à une liste, mais s'écrit avec des ()

paquet = (12, "abc")
paquet
(12, 'abc')
# on ne peut plus y toucher
# paquet[0] = 15 n'est pas autorisé
# ni paquet.append(0)

l’ensemble

une autre forme de container, mais assez différent :

  • comme pour les ensembles mathématiques, un même élément
    ne peut apparaitre qu’une seule fois dans un ensemble

  • la recherche d’un élément dans un ensemble est très efficace
    contrairement aux listes, on n’a pas besoin de balayer tous les éléments repose sur la notion de table de hachage - détaillé dans le cours avancé

  • par contre, limitation sur les éléments
    certains types ne sont pas éligibles par ex. on ne peut pas mettre une liste dans un ensemble
    utiliser à la place un tuple

# pour créer un ensemble
ensemble = {12, "abc"}
ensemble
{12, 'abc'}
# méthode add() pour ajouter
ensemble.add(True)
ensemble
{12, True, 'abc'}
# pas de doublon
ensemble.add("abc")
ensemble
{12, True, 'abc'}
# la recherche est rapide
# bien sûr, c'est surtout intéressant
# sur des grosses données

12 in ensemble
True
# on peut mettre un tuple dans un ensemble
ensemble.add((2, 3))
ensemble
{(2, 3), 12, True, 'abc'}
# et pour enlever
ensemble.remove(12)
ensemble
{(2, 3), True, 'abc'}

itérations sur l’ensemble

forme la plus simple, idem : for .. in ..
attention qu’un ensemble n’a pas d’ordre naturel
depuis Python-3.7 le parcours se fait dans l’ordre des insertions

for item in ensemble:
    print(item)
(2, 3)
True
abc

le dictionnaire

aussi un container, mais cette fois c’est conceptuellement
un ensemble d’associations de la forme

clé → valeur
# la syntaxe pour créer
# un dictionnaire en clair
annuaire = {'alice': 25, 'bob': 32}
# les clés sont ici les 2 chaines
# 'alice', 'bob'

annuaire
{'alice': 25, 'bob': 32}
# on ne peut plus accéder par indice
# annuaire[0] ne veut rien dire!

# par contre on peut accéder par clé
annuaire['bob']
32
# pareil pour écrire
# si la clé est inconnue on l'ajoute

annuaire['eve'] = 40
annuaire
{'alice': 25, 'bob': 32, 'eve': 40}
# si la clé existe déjà
# on écrase la valeur associée
annuaire['alice'] = 50
annuaire
{'alice': 50, 'bob': 32, 'eve': 40}
# pour effacer une clé  
del annuaire['eve']
annuaire
{'alice': 50, 'bob': 32}
annuaire
{'alice': 50, 'bob': 32}
# la recherche d'une clé est aussi rapide
# que la recherche dans les ensembles

'alice' in annuaire
True

digression : affectation multiple

# plutôt que de faire
a = 10
b = 20

print(f"a={a}, b={b}")
a=10, b=20
# on peut faire en Python
a, b = 10, 20

print(f"a={a}, b={b}")
a=10, b=20

dans ce contexte c’est un gadget, mais c’est intéressant parfois
car les termes à droite de = sont tous évalués avant de faire les affectations

# et ainsi on peut par exemple
# échanger deux variables
a, b = b, a

print(f"a={a}, b={b}")
a=20, b=10

itération sur un dictionnaire

même remarque que les ensembles : pas d’ordre naturel
depuis Python-3.7 le parcours se fait dans l’ordre des insertions

for cle, valeur in annuaire.items():
    print(f"{cle}{valeur}")
alice → 50
bob → 32

cette forme est à mettre en rapport avec l’affectation multiple
dans ce sens que ça revient à faire ceci :

# en décomposant un peu pour bien comprendre
for couple in annuaire.items():
    cle, valeur = couple # on appelle cela de l'unpacking
    print(f"{cle}{valeur}")
alice → 50
bob → 32

fonctions et arguments multiples (1)

mécanisme pour définir un nombre quelconque d’arguments à une fonction

# parfois on a envie qu'une fonction puisse
# accepter un nombre variable d'arguments

def foo(fixe, *variable):
    """
    fixe reçoit le premier argument
    variable reçoit un tuple avec tous les autres arguments de l'appel
    """
    print(f"premier argument: {fixe}")
    print(f"les autres: {variable} - de type {type(variable)}")
    for item in variable:
        print(f"item {item}")
foo(1)
premier argument: 1
les autres: () - de type <class 'tuple'>
foo(1, 2)
premier argument: 1
les autres: (2,) - de type <class 'tuple'>
item 2

foo(1, 2, 3)
premier argument: 1
les autres: (2, 3) - de type <class 'tuple'>
item 2
item 3

bien entendu on ne peut définir qu’un seul paramètre de ce genre, et il doit apparaitre en dernier dans la signature de la fonction
Si on pouvait en mettre plusieurs, il y aurait ambigüité quant à qui reçoit quoi.

fonctions et arguments multiples (2)

dans l’autre sens, si j’ai un container avec des objets que je veux passer individuellement à une fonction

# par exemple j'ai une liste
args = [1, 2, 3]

# et en fait je veux appeler
# foo(1, 2, 3)
#
# je pourrais faire
# foo(args[0], args[1], arg[2])
#
# mais bien sûr ça ne marchera
# que si args contient 3 objets
# dans ce cas on peut utiliser à nouveau
# l'étoile, et faire plutôt
foo(*args)
premier argument: 1
les autres: (2, 3) - de type <class 'tuple'>
item 2
item 3
# vérifions que c'est bien
# ce qu'on voulait
foo(1, 2, 3)
premier argument: 1
les autres: (2, 3) - de type <class 'tuple'>
item 2
item 3

à l’appel de la fonction par contre on peut passer plusieurs arguments étoilés, leurs composants sont simplement ajoutés dans l’ordre aux arguments de la fonction.

résumé

Python propose des types prédéfinis

  • list : un container flexible et ordonné, accessible par indice

  • plus accessoirement, tuple pour créer des containers similaires mais non modifiables

  • set : un container non-ordonné, sans doublon, et à recherche rapide

  • dict : un ensemble d’associations clé → valeur,
    à recherche rapide, accessible par clé

  • la forme *args permet aux fonctions d’accepter un nombre quelconque d’arguments

    • définition def foo(*args):

    • appel foo(*args)