Aller au contenu

Construire des classes et leurs comportements

object() : créer un objet minimal

object est la base commune des classes Python. Son constructeur crée un objet minimal, sans dictionnaire permettant d'y ajouter librement des attributs.

first = object()
second = object()
print(first is second)  # False

Il sert notamment à construire une valeur unique signalant une absence :

MISSING = object()
value = {}.get("price", MISSING)
print(value is MISSING)  # True

Equivalent en C#

object first = new object();
object second = new object();
Console.WriteLine(ReferenceEquals(first, second)); // False
object missing = new object();
object value = missing;
Console.WriteLine(ReferenceEquals(value, missing)); // True

new object() permet lui aussi de créer une sentinelle d’identité. Les types valeur .NET peuvent être convertis en object par boxing ; Python ne possède pas la même distinction entre types valeur et types référence.

super() : poursuivre la recherche selon l'ordre de résolution

super() fournit un intermédiaire permettant d'accéder aux implémentations suivantes dans l'ordre de résolution des méthodes (MRO). Dans un héritage simple, cela correspond généralement à la classe parente.

class Animal:
    def describe(self):
        return "Animal"

class Dog(Animal):
    def describe(self):
        return super().describe() + " : chien"

print(Dog().describe())  # Animal : chien

Ce fonctionnement est particulièrement utile pour l'héritage multiple coopératif. La forme sans argument s'emploie dans le corps approprié d'une méthode.

Equivalent en C#

Console.WriteLine(new Dog().Describe());

class Animal
{
    public virtual string Describe() => "Animal";
}
class Dog : Animal
{
    public override string Describe() => base.Describe() + " : chien";
}

base exprime l’accès à la classe de base en C#. Python autorise l’héritage multiple de classes et super() suit le MRO ; C# n’a qu’une classe de base directe. super() ne signifie donc pas simplement « appeler le parent nommé ».

property() : définir une propriété

property() associe l'accès à un attribut à des fonctions de lecture, d'écriture ou de suppression. On l'emploie généralement comme décorateur.

class Product:
    def __init__(self, price):
        self.price = price

    @property
    def price(self):
        return self._price

    @price.setter
    def price(self, value):
        if value < 0:
            raise ValueError("Le prix doit être positif ou nul.")
        self._price = value

product = Product(69.90)
print(product.price)  # 69.9

La forme explicite est property(fget=None, fset=None, fdel=None, doc=None). Sans accesseur d'écriture, la propriété ne peut pas être affectée par ce mécanisme.

Equivalent en C#

var product = new Product(69.90);
Console.WriteLine(product.Price);

class Product
{
    private double _price;
    public Product(double price) { Price = price; }
    public double Price
    {
        get => _price;
        set
        {
            if (value < 0) throw new ArgumentOutOfRangeException(nameof(value));
            _price = value;
        }
    }
}

La propriété C# get / set exprime un besoin très proche. Python s’appuie sur un objet descripteur créé par property. Les deux exemples permettent de valider une affectation ; pour des montants métier, choisissons un type décimal adapté.

classmethod() : définir une méthode de classe

Le décorateur @classmethod transmet la classe comme premier argument, nommé cls par convention. Il convient notamment aux constructeurs alternatifs.

class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    @classmethod
    def from_text(cls, text):
        name, price = text.split(";")
        return cls(name, float(price))

product = Product.from_text("Clavier;69.90")
print(product.name)  # Clavier

Equivalent en C#

var product = Product.FromText("Clavier;69.90");
Console.WriteLine(product.Name);

record Product(string Name, double Price)
{
    public static Product FromText(string text)
    {
        string[] parts = text.Split(';');
        return new Product(parts[0], double.Parse(parts[1], CultureInfo.InvariantCulture));
    }
}

Une fabrique statique fournit un parallèle pratique, mais ne reçoit pas automatiquement le type appelant. En Python, une méthode de classe héritée reçoit la sous-classe dans cls et peut construire celle-ci. La fabrique C# montrée construit toujours Product.

staticmethod() : définir une méthode statique

Le décorateur @staticmethod définit une méthode qui ne reçoit implicitement ni instance ni classe.

class Pricing:
    @staticmethod
    def calculate_total(price, tax_rate=0.20):
        return price * (1 + tax_rate)

print(Pricing.calculate_total(100))  # 120.0

Une fonction de module convient aussi lorsque le rattachement à une classe n'apporte pas de sens particulier.

Equivalent en C#

Console.WriteLine(Pricing.CalculateTotal(100)); // 120

static class Pricing
{
    public static double CalculateTotal(double price, double taxRate = 0.20)
        => price * (1 + taxRate);
}

static est le rapprochement direct pour une méthode sans instance. En Python, le décorateur évite l’injection implicite de self. Les règles d’appel et d’héritage ne sont pas identiques à celles des membres statiques C#.