Aller au contenu

Structures de données

Les principales structures de données sont :

  • Les tuples, tableaux et listes,
  • Les dictionnaires,
  • Les namedtuples et dataclasses,
  • Les classes.

Tuples, tableaux et listes

Python distingue principalement les séquences muables et immuables :

Un tuple (t = tuple()) ne présente ainsi que les méthodes count et index, là où une liste ou un tableau (l = []), tout les deux représentés par [] sont des variables muables. Les méthodes accessibles à une instance de list sont :

  • ‘append’,
  • ‘clear’,
  • ‘copy’,
  • ‘count’,
  • ‘extend’,
  • ‘index’,
  • ‘insert’,
  • ‘pop’,
  • ‘remove’,
  • ‘reverse’,
  • ‘sort’

L’empreinte d’un tuple sera également plus faible qu’une liste. Choisissez en fonction de vos besoins.

Named tuples

Comme vu ci-dessus, vous pourriez avoir besoin d’un tuple lorsque vous avez besoin d’une empreinte mémoire plus faible qu’une liste, ou simplement pour retourner un ensemble défini de valeurs à partir d’une fonction. Django propose ainsi quelques fonctions comme le get_or_create(), qui retourne l’object construit ou récupéré (get), et s’il a dû être créé ou non. Plus concrètement, nous récupérons ainsi deux valeurs immuables, dont la seconde indique si l’objet retourné existait auparavant ou non.

from models import Library
object, created = Library.objects.get_or_create(title="The Subtle Art of Not Giving a F*ck")

L’intérêt de ceci tient en un mot : simplicité. La simplicité peut cependant devenir un frein à la maintenabilité, raison pour laquelle un tuple peut nécessiter un brin de refactoring. La solution consiste alors à passer par un namedtuple, qui permet d’associer une nomenclature au niveau des propriétés de la structure, tout en gardant ses possibilités d’indexation.

>>> Point = namedtuple('Point', ['x', 'y', 'z'])
>>> p = Point(1, 2, 3)
>>> print(p.y)
2

Références : https://snarky.ca/dont-use-named-tuples-in-new-apis/

Dictionnaires

Un dictionnaire ({}) correspond à une table de hash (hashmap) dans d’autres langages.

Il associe une clé à une valeur, au travers d’une table de correspondances. L’accès à chaque clé est ainsi rendu très rapide, au détour d’une éventuelle complexité à l’utilisation.

L’accès à une clé qui n’existe pas lèvera une exception de type KeyError :

>>> canevas = dict() # identique à canevas = {}
>>> canevas["a"]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'a'

Pour palier à ceci, nous pouvons utiliser la méthode get(), qui permet de spécifier une valeur par défaut si la clé venait à ne pas exister :

>>> canevas = {}
>>> canevas.get("a", "abracadabra")
'abracadabra'

Une des méthodes les plus pratiques est la méthode setdefault, qui retourne la valeur de la clé si elle existe, et crée une nouvelle occurrence (avec une valeur par défaut) si elle n’existait pas encore :

>>> d = {}
>>> d.setdefault(1, "Yolo") # on cherche la clé "1"
'Yolo'
>>> d
{1: 'Yolo'}
>>> d.setdefault(1, "Yulu")
'Yolo'

Ceci évite, dans une boucle, de vérifier si la clé existe déjà, et si pas, de la créer.

Classes

Une classe se concentre sur le comportement d’un élément :

from decimal import Decimal
class ShoppingCart:
def __init__(self):
self.items = []
def add_item(self, item: str, price: Decimal):
self.items.append({"item": item,"price": price})
def total(self) -> Decimal:
return sum(item["price"] for item in self.items)
cart = ShoppingCart()
cart.add_item("apple", Decimal("1.5"))
cart.add_item("banana", Decimal("2.0"))
print(f"${cart.total():.2f}")
$3.50

Dataclasses

Les dataclasses permettent surtout d’éviter le boilerplate lié aux classes : constructeurs, méthodes, …

from dataclasses import dataclass
from decimal import Decimal
@dataclass
class Item:
name: str
price: Decimal
item = Item(name="apple", price=Decimal("1.5"))
print(item)
print(f"${item.price:.2f}")
Item(name='apple', price=Decimal('1.5'))
$1.50