Skip to article frontmatterSkip to article content

plusieurs types pratiques et efficaces sont fournis de base, et notamment

problèmes avec les séquences

de deux ordres principalement

(1) les recherches sont lentes

a = list(range(30000000))
'x' in a      # c’est long !
False

(2) on ne peut indexer que par un entier

a[3]          # on peut utiliser un indice entier
3
a = []
# on ne peut pas indexer avec un nom ou autre chose qu'un entier
try:
    a['alice'] = 10
except TypeError as e:
    print("OOPS", e)
OOPS list indices must be integers or slices, not str

récapitulons

la solution : les tables de hash

le principe

la fonction de hash

table de hash et Python

le set

création

# ATTENTION : les dictionnaires étaient là avant les ensembles !
# du coup {} n'est pas un ensemble, mais un dict !

set()          # ensemble vide
set()
# ou sinon, on peut comme toujours
# utiliser le type comme une
# usine à objets

L1 = [1, 2, 3, 1, 1, 6, 4]
S1 = set(L1)
S1
{1, 2, 3, 4, 6}

opérations sur set

S1
{1, 2, 3, 4, 6}
L2 = [3, 4, 1]
S2 = set(L2)
S2
{1, 3, 4}
4 in S2
True
S1 - S2            # différence
{2, 6}
S1 | S2            # union
{1, 2, 3, 4, 6}
S1 & S2            # intersection
{1, 3, 4}
# toujours vrai

(S1 & S2) | (S1 - S2) | (S2 - S1) == (S1 | S2)
True

le set: méthodes

les plus utiles sont add() et .remove() (et là encore il y en a toute un paquet...)

# ensemble littéral
S3 = {1, 2, 3, 4}
S3
{1, 2, 3, 4}
# ajout d'un élément

S3.add('spam')
S3
{1, 2, 3, 4, 'spam'}
# pas de duplication
# et pas d'ordre particulier
S3.update([10, 11, 10, 11])
S3
{1, 10, 11, 2, 3, 4, 'spam'}
S3.remove(11)
S3
{1, 10, 2, 3, 4, 'spam'}

le set est mutable

fs = frozenset([1, 2, 3, 4])
# frozenset pas mutable
try:
    fs.add(5)
except AttributeError as e:
    print("OOPS", e)
OOPS 'frozenset' object has no attribute 'add'

éléments acceptables

S = set()
S.add(1)
S.add("abc")
# je peux y ajouter un tuple !
S.add((1, 2))
S
{(1, 2), 1, 'abc'}
# mais pas une liste !
try:
    S.add([1, 2])
except TypeError as e:
    print("OOPS", e)
OOPS unhashable type: 'list'

rapide test de performance

pour la recherche d’un élément, le set est beaucoup plus rapide

x = list(range(100000))

%timeit -n 300 "c" in x
1.71 ms ± 1.05 ms per loop (mean ± std. dev. of 7 runs, 300 loops each)
x = set(range(100000))

%timeit -n 300 "c" in x
18.3 ns ± 0.754 ns per loop (mean ± std. dev. of 7 runs, 300 loops each)

le set est beaucoup plus rapide

et cela même si la liste est très petite

x = list(range(2))

%timeit -n 300 "c" in x
62.6 ns ± 1.83 ns per loop (mean ± std. dev. of 7 runs, 300 loops each)
x = set(range(2))

%timeit -n 300 "c" in x
19.1 ns ± 1.07 ns per loop (mean ± std. dev. of 7 runs, 300 loops each)

le meilleur cas pour la liste

en fait pour obtenir des performances comparables

x = list(range(2))

%timeit -n 300 0 in x
26.7 ns ± 0.878 ns per loop (mean ± std. dev. of 7 runs, 300 loops each)
x = set(range(2))

%timeit -n 300 0 in x
24.4 ns ± 0.909 ns per loop (mean ± std. dev. of 7 runs, 300 loops each)

le dictionnaire

création

# ATTENTION : les dictionnaires étaient là avant les ensembles !
# du coup {} n'est pas un ensemble, mais un dict !

D = {}
D
{}
# un dictionnaire créé de manière littérale

{ 'douze' : 12, 1: 'un', 'liste' : [1, 2, 3] }
{'douze': 12, 1: 'un', 'liste': [1, 2, 3]}
# une autre façon, quand les clés sont des chaînes

dict( a = 'A', b = 'B')
{'a': 'A', 'b': 'B'}
# ou aussi, plus rare, mais à partir d'une liste de couples...
# juste pour montrer qu'il y a souvent plein de façons de faire...

dict( [ ('a', 'A'), ('b', 'B') ] )
{'a': 'A', 'b': 'B'}

manipulations usuelles

D = {'alice': 35, 'bob' : 9, 'charlie': 6}
D
{'alice': 35, 'bob': 9, 'charlie': 6}
# combien d'entrées

len(D)
3
D['alice']
35
# appartenance = est-ce une clé ?
'bob' in D
True
D['jim'] = 32
D
{'alice': 35, 'bob': 9, 'charlie': 6, 'jim': 32}
# on n'avait pas encore vu cet opérateur..

del D['jim']
D
{'alice': 35, 'bob': 9, 'charlie': 6}

D.get()

notez bien que D[clef] lance une exception si la clé n’est pas présente
une alternative - sans exception - est d’utiliser la méthode get()

# la clé 'marc' n'est pas présente

# plutôt que cette vilaine circonlocution...

try:
    x = D['marc']
except KeyError as e:
    x = "?"

x
'?'
# c'est quand même plus lisible comme ça:

D.get('marc', '?')
'?'

itération sur un dictionnaire

# l'idiome pour itérer sur
# un dictionnaire

for k, v in D.items():
    print(f"{k=} {v=}")
k='alice' v=35
k='bob' v=9
k='charlie' v=6

qu’est-ce qu’une vue ?

clefs = D.keys()

clefs
dict_keys(['alice', 'bob', 'charlie'])
# ici clefs est une vue
del D['bob']

clefs
dict_keys(['alice', 'charlie'])

méthodes sur les dictionnaires

ici à nouveau, il y a plein d’autres méthodes très utiles, à découvrir ici

création automatique de valeurs (avancé)

from collections import defaultdict

# ici je décide que les valeurs sont des listes
dd = defaultdict(list)

# pas d'exception alors que la clé 0 est absente
# au contraire on appelle list() pour l'initialiser
dd[0].append(1)
dd[0].append(2)
dd[0]
[1, 2]