OUJOOD.COM
Dans beaucoup de langages orientés objet, les attributs d'une classe sont systématiquement cachés derrière des méthodes get_valeur() et set_valeur(). En Python, cette convention n'est pas la norme. On commence par des attributs publics simples, et on ajoute du contrôle uniquement quand c'est nécessaire — sans casser le code existant.
C'est précisément ce que permet @property : transformer un attribut en propriété contrôlée, avec de la validation ou du calcul à l'accès ou à l'assignation, tout en conservant la syntaxe objet.attribut. Le code appelant ne voit aucune différence.
Le problème : accès direct sans contrôle
Sans propriété, rien n'empêche d'assigner n'importe quelle valeur à un attribut, y compris des valeurs absurdes.
class Personne:
def __init__(self, nom, age):
self.nom = nom
self.age = age
p = Personne("Alice", 30)
p.age = -5
# Python accepte sans broncher — problème !
print(p.age)
# Résultat : -5
La solution : @property pour un getter
@property transforme une méthode en attribut en lecture. On accède à la valeur avec objet.age comme si c'était un attribut normal, mais Python exécute en réalité la méthode.
class Personne:
def __init__(self, nom, age):
self.nom = nom
self._age = age # Convention : _ signale un attribut "interne"
@property
def age(self):
return self._age
p = Personne("Alice", 30)
print(p.age)
# Résultat : 30 — accès comme un attribut normal
# Tentative de modification → erreur car pas de setter défini
# p.age = 31 → AttributeError: can't set attribute
Ajouter un setter avec @property.setter
Pour autoriser la modification tout en validant la valeur, ajoutez un setter avec le décorateur @nom_propriete.setter.
class Personne:
def __init__(self, nom, age):
self.nom = nom
self.age = age # Appelle directement le setter
@property
def age(self):
return self._age
@age.setter
def age(self, valeur):
if not isinstance(valeur, int) or valeur < 0 or valeur > 150:
raise ValueError(f"Âge invalide : {valeur}")
self._age = valeur
p = Personne("Alice", 30)
print(p.age)
# Résultat : 30
p.age = 31
print(p.age)
# Résultat : 31
try:
p.age = -5
except ValueError as e:
print(e)
# Résultat : Âge invalide : -5
Remarquez que dans __init__, on écrit self.age = age (sans underscore) — cela passe par le setter et déclenche la validation dès la création de l'objet.
Ajouter un deleter avec @property.deleter
Le deleter est appelé quand on utilise del objet.attribut. C'est moins fréquent, mais utile pour nettoyer des ressources ou remettre un attribut à son état initial.
class Personne:
def __init__(self, nom, age):
self.nom = nom
self.age = age
@property
def age(self):
return self._age
@age.setter
def age(self, valeur):
if valeur < 0:
raise ValueError("L'âge ne peut pas être négatif.")
self._age = valeur
@age.deleter
def age(self):
print("Suppression de l'âge.")
del self._age
p = Personne("Alice", 30)
del p.age
# Résultat : Suppression de l'âge.
Propriété calculée (read-only)
Une propriété n'a pas forcément besoin de stocker une valeur — elle peut la calculer à la volée depuis d'autres attributs. Dans ce cas, on définit uniquement le getter, sans setter.
class Rectangle:
def __init__(self, largeur, hauteur):
self.largeur = largeur
self.hauteur = hauteur
@property
def aire(self):
# Calculée à chaque accès, aucun stockage
return self.largeur * self.hauteur
@property
def perimetre(self):
return 2 * (self.largeur + self.hauteur)
r = Rectangle(5, 3)
print(r.aire)
# Résultat : 15
print(r.perimetre)
# Résultat : 16
r.largeur = 10
print(r.aire)
# Résultat : 30 — recalculée automatiquement
Exemple complet : classe Température
Un exemple plus concret : une classe qui stocke une température en Celsius, avec une propriété calculée pour Fahrenheit et une validation à l'assignation.
class Temperature:
ABSOLU_ZERO = -273.15
def __init__(self, celsius):
self.celsius = celsius # Passe par le setter
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, valeur):
if valeur < Temperature.ABSOLU_ZERO:
raise ValueError(
f"Température inférieure au zéro absolu ({Temperature.ABSOLU_ZERO}°C)."
)
self._celsius = valeur
@property
def fahrenheit(self):
# Propriété calculée, lecture seule
return self._celsius * 9/5 + 32
def __str__(self):
return f"{self._celsius}°C / {self.fahrenheit:.1f}°F"
t = Temperature(100)
print(t)
# Résultat : 100°C / 212.0°F
t.celsius = 0
print(t.fahrenheit)
# Résultat : 32.0
try:
t.celsius = -300
except ValueError as e:
print(e)
# Résultat : Température inférieure au zéro absolu (-273.15°C).
Quand utiliser @property ?
Quelques règles simples pour décider :
- Commencez par des attributs publics simples. N'ajoutez pas de propriétés par habitude ou par précaution.
- Ajoutez @property quand vous avez besoin de validation — empêcher les valeurs négatives, vérifier un format, lever une exception explicite.
- Utilisez une propriété calculée quand une valeur se déduit toujours d'autres attributs et qu'il n'y a aucune raison de la stocker séparément.
- Ne renommez pas vos attributs juste pour ajouter des underscores. L'underscore simple (
_attribut) signale une convention "interne" — Python ne l'applique pas de force.
Par carabde | Mis à jour le 21 avril 2026