Aller au contenu

Définir des modèles de données

dataclasses : classes centrées sur les données

Le décorateur @dataclass peut générer notamment __init__(), __repr__() et __eq__() à partir des champs déclarés.

from dataclasses import dataclass, field, asdict

@dataclass
class Product:
    name: str
    price: float
    tags: list[str] = field(default_factory=list)

product = Product("Clavier", 69.90)
print(asdict(product))
# {'name': 'Clavier', 'price': 69.9, 'tags': []}

default_factory crée une nouvelle liste pour chaque instance. replace() construit une autre instance en modifiant certains champs. frozen=True bloque les affectations usuelles aux champs, sans rendre immuables tous les objets qu'ils contiennent. Les annotations ne valident pas automatiquement les valeurs à l'exécution.

Parallèle C# et .NET

var product = new Product("Clavier", 69.90);
var other = product with { Price = 79.90 };
Console.WriteLine(other.Name);

public record Product(string Name, double Price)
{
    public List<string> Tags { get; init; } = new();
}

Un record C# génère une représentation et une égalité centrées sur les données. with crée une copie superficielle : Tags reste partagé ici. Les deux modèles ne sont pas identiques ; notamment l’égalité des listes est structurelle en Python, mais repose par défaut sur la référence des List<T> en C#. asdict() n’a pas de correspondance automatique dans ce record.

enum : ensembles de valeurs nommées

from enum import Enum, auto

class OrderStatus(Enum):
    CREATED = auto()
    PAID = auto()
    SHIPPED = auto()

status = OrderStatus.PAID
print(status.name)                    # PAID
print(status is OrderStatus.PAID)     # True

Enum définit les membres et auto() demande une valeur automatique. IntEnum s'intègre aux entiers ; Flag sert aux combinaisons de drapeaux. Une énumération permet de remplacer des chaînes dispersées par des valeurs explicites.

Parallèle C# et .NET

OrderStatus status = OrderStatus.Paid;
Console.WriteLine(status);                   // Paid
Console.WriteLine(status == OrderStatus.Paid); // True

enum OrderStatus
{
    Created = 1,
    Paid = 2,
    Shipped = 3
}

Les valeurs commencent explicitement à 1 pour correspondre à l’usage courant de auto() dans Enum Python ; un enum C# sans initialisation commence à 0. C# autorise par cast des valeurs numériques non déclarées. [Flags] se rapproche de Flag, mais les règles de typage et d’opérations restent différentes.

typing : préciser les contrats de types

typing fournit notamment Any, Literal, TypedDict, Protocol et cast() pour les annotations et l'analyse statique.

from typing import TypedDict, Literal

class ProductData(TypedDict):
    name: str
    category: Literal["hardware", "software"]

product: ProductData = {"name": "Clavier", "category": "hardware"}
print(product["name"])

À l'exécution, cet objet reste un dictionnaire ordinaire. Un analyseur comme Mypy peut vérifier le contrat ; Python ne le contrôle pas automatiquement. Pour les collections usuelles, préférons les formes modernes list[str], dict[str, int] et str | None plutôt que les anciens alias correspondants.

Parallèle C# et .NET

var product = new ProductData("Clavier", ProductCategory.Hardware);
Console.WriteLine(product.Name);

public enum ProductCategory { Hardware, Software }
public record ProductData(string Name, ProductCategory Category);

Le contrat est ici un type CLR réel, vérifié principalement par le compilateur. Un TypedDict Python reste un dictionnaire à l’exécution. Protocol utilise un typage structurel dans les outils d’analyse ; une interface C# exige normalement une implémentation déclarée. typing.cast() ne convertit pas la valeur à l’exécution, contrairement à de nombreux casts C#.