Contactez-nous

Propriétés (@property) : contrôler l'accès aux attributs

Maîtrisez les propriétés en Python avec le décorateur @property. Transformez vos méthodes en attributs, définissez des accesseurs (getters), des mutateurs (setters) et des suppresseurs (deleters) pour un contrôle précis de l'accès et de la modification de

Qu'est-ce qu'une propriété ? Un attribut géré par des méthodes

En Python, une propriété est un type spécial d'attribut qui est géré par des méthodes (accesseur, mutateur, suppresseur). Au lieu d'accéder directement à un attribut, vous utilisez des méthodes qui sont appelées automatiquement lorsque vous accédez, modifiez ou supprimez l'attribut.

Les propriétés permettent de :

  • Contrôler l'accès aux attributs : Vous pouvez valider les valeurs, effectuer des calculs, ou déclencher d'autres actions lorsque l'attribut est accédé, modifié ou supprimé.
  • Encapsuler les données : Vous pouvez masquer l'implémentation interne de l'attribut et exposer une interface publique propre.
  • Maintenir l'intégrité des données : Vous pouvez vous assurer que les attributs restent dans un état valide.
  • Créer des attributs en lecture seule, en écriture seule, ou calculés : Vous avez un contrôle total sur la manière dont les attributs sont manipulés.

Les propriétés sont un moyen élégant et "pythonique" de gérer l'accès aux attributs, tout en respectant les principes de l'encapsulation.

Le décorateur @property : transformer une méthode en attribut

La manière la plus courante de créer une propriété en Python est d'utiliser le décorateur `@property`. Ce décorateur transforme une méthode en un attribut en lecture seule.

Exemple :

class Personne:
    def __init__(self, nom, prenom):
        self._nom = nom
        self._prenom = prenom

    @property
    def nom_complet(self):
        """Retourne le nom complet de la personne."""
        return f"{self._prenom} {self._nom}"

p = Personne("Dupont", "Alice")
print(p.nom_complet)  # Accès à la propriété nom_complet (comme un attribut) : affiche "Alice Dupont"

Dans cet exemple :

  • `_nom` et `_prenom` sont des attributs "protégés" (convention du simple soulignement).
  • `nom_complet` est une propriété. Le décorateur `@property` transforme la méthode `nom_complet` en un attribut en lecture seule.
  • Vous pouvez accéder à `p.nom_complet` comme s'il s'agissait d'un attribut, mais en réalité, c'est la méthode `nom_complet` qui est appelée.

La propriété `nom_complet` est un attribut *calculé* : sa valeur est calculée à partir d'autres attributs (`_nom` et `_prenom`).

Accesseurs (getters), mutateurs (setters) et suppresseurs (deleters)

Vous pouvez définir des méthodes spéciales pour gérer non seulement l'accès à une propriété (accesseur, ou "getter"), mais aussi sa modification (mutateur, ou "setter") et sa suppression (suppresseur, ou "deleter").

Pour cela, vous utilisez le nom de la propriété (celle définie avec `@property`) suivi de `.setter` ou `.deleter`, comme décorateurs pour les méthodes correspondantes.

Syntaxe :

class MaClasse:
    def __init__(self, valeur):
        self._valeur = valeur

    @property
    def valeur(self):
        """Accesseur (getter) pour l'attribut valeur."""
        # ... Code pour accéder à la valeur (par exemple, validation, calcul, etc.) ...
        return self._valeur

    @valeur.setter
    def valeur(self, nouvelle_valeur):
        """Mutateur (setter) pour l'attribut valeur."""
        # ... Code pour modifier la valeur (par exemple, validation, calcul, etc.) ...
        self._valeur = nouvelle_valeur

    @valeur.deleter
    def valeur(self):
        """Suppresseur (deleter) pour l'attribut valeur."""
        # ... Code pour supprimer la valeur (par exemple, nettoyage, etc.) ...
        del self._valeur
  • La méthode décorée avec `@property` est l'accesseur (getter). Elle est appelée lorsque vous *accédez* à la propriété.
  • La méthode décorée avec `@nom_propriete.setter` est le mutateur (setter). Elle est appelée lorsque vous *modifiez* la propriété. Elle prend en argument la nouvelle valeur.
  • La méthode décorée avec `@nom_propriete.deleter` est le suppresseur (deleter). Elle est appelée lorsque vous *supprimez* la propriété (avec `del`).

Vous n'êtes pas obligé de définir les trois méthodes. Si vous ne définissez pas de mutateur, la propriété sera en lecture seule. Si vous ne définissez pas de suppresseur, la propriété ne pourra pas être supprimée.

Exemple complet : contrôle de l'âge d'une personne

Voici un exemple complet qui illustre l'utilisation des propriétés pour contrôler l'accès à l'âge d'une personne :

class Personne:
    def __init__(self, age):
        self._age = age  # Attribut "protégé"

    @property
    def age(self):
        """L'âge de la personne (en années)."""
        return self._age

    @age.setter
    def age(self, valeur):
        if not isinstance(valeur, int):
            raise TypeError("L'âge doit être un entier.")
        if valeur < 0:
            raise ValueError("L'âge ne peut pas être négatif.")
        if valeur > 150:
            raise ValueError("L'âge ne peut pas être supérieur à 150.")
        self._age = valeur

    @age.deleter
    def age(self):
        print("Suppression de l'âge...")
        del self._age

# Utilisation
p = Personne(30)
print(p.age)  # Accès à l'âge : affiche 30
p.age = 40    # Modification de l'âge
# p.age = -5   # Lèverait une ValueError
# p.age = "abc" # Lèverait une TypeError
del p.age    # Suppression de l'âge

Dans cet exemple :

  • `_age` est un attribut "protégé".
  • `age` est une propriété avec un accesseur, un mutateur et un suppresseur.
  • L'accesseur retourne simplement la valeur de `_age`.
  • Le mutateur valide la nouvelle valeur (vérifie qu'il s'agit d'un entier positif et inférieur à 150) avant de modifier `_age`.
  • Le suppresseur affiche un message et supprime `_age`.

Les propriétés permettent de contrôler précisément l'accès aux attributs et de garantir que les données restent cohérentes.

Propriétés vs. méthodes : quand utiliser l'une ou l'autre ?

Quand faut-il utiliser une propriété plutôt qu'une méthode ordinaire ?

  • Utilisez une propriété :
    • Lorsque vous voulez contrôler l'accès à un attribut (validation, calcul, etc.).
    • Lorsque vous voulez masquer le fait que vous accédez à un attribut (abstraction).
    • Lorsque vous voulez créer un attribut en lecture seule, en écriture seule, ou calculé.
    • Lorsque vous voulez que l'accès à l'attribut ressemble à un accès à un attribut ordinaire (syntaxe plus simple).
  • Utilisez une méthode :
    • Lorsque l'opération effectue une action significative (autre que simplement obtenir, définir ou supprimer une valeur).
    • Lorsque l'opération prend des arguments (autres que la valeur à affecter dans le cas d'un mutateur).
    • Lorsque l'opération a des effets de bord (modifie l'état de l'objet d'une manière qui n'est pas directement liée à l'attribut).
    • Lorsque l'opération est coûteuse en temps de calcul (il est préférable de signaler explicitement qu'il s'agit d'une opération coûteuse en utilisant une méthode).

En résumé, utilisez les propriétés pour gérer l'accès aux attributs de manière contrôlée et transparente, et utilisez les méthodes pour les actions plus complexes.