Constructions classiques#

Le module collections propose d’autres types d’objets qui en contiennent d’autres. Ils sont très proches des types standards liste ou dictionnaires mais sont optimisés pour un usage en particulier. Cela les rend plus efficaces pour celui-ci et souvent plus lent pour les autres. Ce chapitre en recense les plus utilisés.

OrderedDict#

collections.OrderedDict est un dictionnaire qui conservent l’ordre dans lequel les éléments y ont été insérés. Cette distinction était effective jusqu’à Python 3.7 mais depuis cette version, le type standard dict conserve également l’ordre d’insertion comme le montre l’exemple suivant.

<<<

import random
from collections import OrderedDict

rnd = [random.randint(0, 10) for i in range(10)]

dico = {}
for i, h in enumerate(rnd):
    dico[h] = i
print(list(dico))
print(list(dico.items()))

dico = OrderedDict()
for i, h in enumerate(rnd):
    dico[h] = i
print(list(dico))
print(list(dico.items()))

>>>

    [3, 2, 8, 10, 4, 0, 1]
    [(3, 5), (2, 1), (8, 2), (10, 7), (4, 6), (0, 8), (1, 9)]
    [3, 2, 8, 10, 4, 0, 1]
    [(3, 5), (2, 1), (8, 2), (10, 7), (4, 6), (0, 8), (1, 9)]

Néanmoins, cette distinctions est importante à connaître pour des langages plus bas niveau comme le C++.

namedtuple#

collections.namedtuple() est un tuple, donc immuable, mais chaque élément est associé un nom. La fonction collections.namedtuple() retourne une classe et non une instance de classe.

<<<

from collections import namedtuple

ClassePersonne = namedtuple("Personne", ["nom", "prenom"])

p = ClassePersonne("George", "Boole")
print(p)
print(p.nom, p.prenom)
print(p[0], p[1])

try:
    p.nom = "A"
except AttributeError as e:
    print(e)

>>>

    Personne(nom='George', prenom='Boole')
    George Boole
    George Boole
    can't set attribute

On accède à chaque élément avec un nom ou un indice. Et on peut récupérer la liste des attributs de la classe.

<<<

from collections import namedtuple

ClassePersonne = namedtuple("Personne", ["nom", "prenom"])
print(ClassePersonne._fields)

>>>

    ('nom', 'prenom')

Ce type de construction ne change pas un programme mais elle le rend plus lisible.

Counter#

collections.Counter est un dictionnaire spécifique dans les valeurs sont entières. Il est très pratique pour compter les éléments. L’exemple comptage s’écrit en une ligne.

<<<

from collections import Counter

ensemble = ["A", "B", "A", "AA", "C"]
c = Counter(ensemble)
print(c)

>>>

    Counter({'A': 2, 'B': 1, 'AA': 1, 'C': 1})

deque#

collections.deque est une liste qui supporte l’insertion d’éléments en bout de liste et au début également (liste chaînée).

<<<

from collections import deque

ens = deque(["A", "B"])
ens.append("C")
ens.appendleft("D")
print(ens)

>>>

    deque(['D', 'A', 'B', 'C'])

Il faut retenir que ce qu’on gagne en agilité se perd souvent en performance ou en espace mémoire. La différence n’est pas flagrante. Encore une fois, le langage Python est lent et rend ces différences parfois négligeables. Ces différences sont souvent significatives pour des langages plus bas niveau.

<<<

import sys
from collections import deque
import timeit


def append_time_list():
    ens = list()
    for i in range(0, 10000):
        ens.append(i)
    return ens


def append_time_deque():
    ens = deque()
    for i in range(0, 10000):
        ens.append(i)
    return ens


print("list", timeit.timeit(append_time_list, number=100))
print("deque", timeit.timeit(append_time_deque, number=100))

>>>

    list 0.04626930000085849
    deque 0.046163399999386456