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 dataclassfrom decimal import Decimal
@dataclassclass 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