Liste, tuple, ensemble, dictionnaire, liste chaînée, coût des opérations#

Exemples de containers, list, tuple, set, dict.

Python propose différents containers pour stocker des éléments. Voici les plus courants :

  • list : tableau d’éléments indexés de 0 à n exclu auquel on peut ajouter ou retirer des éléments

  • dict : tableau d’éléments indexés par des types immuables auquel on peut ajouter ou retirer des éléments

  • tuple : tableau d’éléments indexés de 0 à n exclu qu’on ne peut pas modifier

  • set : tableau d’éléments uniques non indexés

  • frozenset : set immuables (non modifiable)

  • deque : presque équivalent à une listes, la différent vient de l’implémentation, les mêmes opérations n’auront pas les mêmes coûts (deque = liste chaînée)

D’autres containers sont disponibles via le module collections. Tous proposent de stocker un nombre variables d’éléments. Deux aspects difféèrent :

  • la façon de désigner un élément de l’ensemble

  • le coût de certaines opérations, il faut choisir qui minisera le coût des opérations pour votre programme

Insertion avec list et deque#

On veut comparer les coûts d’insertion en début et fin de liste pour un grand nombre d’éléments.

[1]:
import time, collections

N = 1000000

for p in range(0, 3):
    print("passage ", p)
    print("  insertion en fin")

    li = list()
    a = time.perf_counter()
    for i in range(0, N):
        li.append(i)
    b = time.perf_counter()
    print("    list", N, "éléments, temps par éléments :", (b - a) / N)

    li = collections.deque()
    a = time.perf_counter()
    for i in range(0, N):
        li.append(i)
    b = time.perf_counter()
    print("    deque", N, "éléments, temps par éléments :", (b - a) / N)

    print("  insertion au début")
    li = collections.deque()
    a = time.perf_counter()
    for i in range(0, N):
        li.appendleft(i)
    b = time.perf_counter()
    print("    deque", N, "éléments, temps par éléments :", (b - a) / N)

    N2 = N // 100
    li = list()
    a = time.perf_counter()
    for i in range(0, N2):
        li.insert(0, i)
    b = time.perf_counter()
    print("    list", N, "éléments, temps par éléments :", (b - a) / N)
passage  0
  insertion en fin
    list 1000000 éléments, temps par éléments : 1.2462739999955374e-07
    deque 1000000 éléments, temps par éléments : 1.0353370000029826e-07
  insertion au début
    deque 1000000 éléments, temps par éléments : 9.81406999999308e-08
    list 1000000 éléments, temps par éléments : 1.796050000029936e-08
passage  1
  insertion en fin
    list 1000000 éléments, temps par éléments : 9.953019999920797e-08
    deque 1000000 éléments, temps par éléments : 8.313129999987723e-08
  insertion au début
    deque 1000000 éléments, temps par éléments : 7.568269999956101e-08
    list 1000000 éléments, temps par éléments : 1.580999999987398e-08
passage  2
  insertion en fin
    list 1000000 éléments, temps par éléments : 8.447889999933977e-08
    deque 1000000 éléments, temps par éléments : 8.401670000057492e-08
  insertion au début
    deque 1000000 éléments, temps par éléments : 7.71205999999438e-08
    list 1000000 éléments, temps par éléments : 1.6167699999641626e-08

On voit que l’insertion au début du tableau est beaucoup plus coûteuse pour une liste que pour un deque.

Un élément dans un ensemble#

Faut-il écrire i in [0,1] ou i in (0,1) ou … Essayons.

[2]:
import time, collections

N = 100000
lens = list(range(0, 1000))
tens = tuple(lens)
sens = set(lens)
fens = frozenset(lens)

for p in range(0, 3):
    print("passage", p)
    a = time.perf_counter()
    s = 0
    for i in range(0, N):
        if i in lens:
            s += 1
    b = time.perf_counter()
    print("  list", N, "fois, temps par éléments :", (b - a) / N)

    a = time.perf_counter()
    s = 0
    for i in range(0, N):
        if i in tens:
            s += 1
    b = time.perf_counter()
    print("  tuple", N, "fois, temps par éléments :", (b - a) / N)

    a = time.perf_counter()
    s = 0
    for i in range(0, N):
        if i in sens:
            s += 1
    b = time.perf_counter()
    print("  set", N, "fois, temps par éléments :", (b - a) / N)

    a = time.perf_counter()
    s = 0
    for i in range(0, N):
        if i in fens:
            s += 1
    b = time.perf_counter()
    print("  frozenset", N, "fois, temps par éléments :", (b - a) / N)
passage 0
  list 100000 fois, temps par éléments : 5.977897000002485e-06
  tuple 100000 fois, temps par éléments : 6.178353999994215e-06
  set 100000 fois, temps par éléments : 6.823300000178278e-08
  frozenset 100000 fois, temps par éléments : 7.471699999769044e-08
passage 1
  list 100000 fois, temps par éléments : 5.712876000006872e-06
  tuple 100000 fois, temps par éléments : 5.798504000003959e-06
  set 100000 fois, temps par éléments : 8.035500000005414e-08
  frozenset 100000 fois, temps par éléments : 8.195899999918766e-08
passage 2
  list 100000 fois, temps par éléments : 5.84480600000461e-06
  tuple 100000 fois, temps par éléments : 5.923587000006591e-06
  set 100000 fois, temps par éléments : 7.347799999479321e-08
  frozenset 100000 fois, temps par éléments : 6.471000000601634e-08

Il apparaît que les ensemble set ou frozenset sont beaucoup plus rapides. Plus l’ensemble est grand, plus cette différence est importante.

[ ]:


Notebook on github