@property
Transforme une méthode en attribut : la valeur est calculée à la lecture, et un setter optionnel contrôle l’écriture.
Exemple
#class Student:
def __init__(self, name, points=0):
self.name = name
self.points = points
@property
def level(self):
return self.points // 100 + 1
léa = Student("Léa", 250)
print(léa.level)
léa.points += 100
print(léa.level)
try:
léa.level = 10
except AttributeError as error:
print(error)Définition et utilisation
#Le décorateur @property permet de lire une méthode comme un simple attribut, sans parenthèses : student.level au lieu de student.level(). La valeur est calculée à chaque lecture, donc elle correspond toujours à l’état actuel de l’objet, par exemple un niveau calculé à partir des points.
Une propriété sans setter est en lecture seule, et toute tentative d’affectation lève une AttributeError. Pour autoriser une écriture contrôlée, ajoutez une seconde méthode du même nom avec le décorateur @nom.setter. Le setter peut vérifier ou transformer la nouvelle valeur avant de l’enregistrer dans un attribut précédé d’un underscore, par ex. self._points.
Les propriétés permettent de commencer par un attribut ordinaire et d’ajouter une validation plus tard, sans modifier le code qui utilise la classe. C’est pourquoi on n’écrit pas par avance, en Python, des méthodes get_points() et set_points() comme en Java. Le décorateur functools.cached_property mémorise le résultat d’un calcul coûteux qui ne change pas.
Syntaxe
#class Nom:
@property
def attribut(self):
return self._attribut
@attribut.setter
def attribut(self, valeur):
self._attribut = valeurÉléments de la propriété
#@property
objet.x
Le getter, la méthode exécutée à la lecture. Son résultat devient la valeur de l’attribut.@x.setter
objet.x = v
Le setter, la méthode exécutée lors de l’affectation. Elle reçoit la nouvelle valeur comme paramètre. Sans lui, l’attribut est en lecture seule.@x.deleter
del objet.x
La méthode exécutée lors de la suppression de l’attribut. Rarement nécessaire.
Plus d’exemples
#class Student:
def __init__(self, name, points=0):
self.name = name
self.points = points
@property
def points(self):
return self._points
@points.setter
def points(self, value):
if value < 0:
raise ValueError("Les points ne peuvent pas être négatifs")
self._points = value
lucas = Student("Lucas", 40)
lucas.points += 25
print(lucas.points)
try:
lucas.points = -5
except ValueError as error:
print(error)
print(lucas.points)L’affectation self.points = points dans __init__ passe elle aussi par le setter, donc il est même impossible de créer un objet avec un nombre de points négatif.
from functools import cached_property
class Course:
def __init__(self, title, lessons):
self.title = title
self.lessons = lessons
@cached_property
def total_minutes(self):
print("Calcul de la durée totale...")
return sum(self.lessons)
python = Course("Python", [15, 20, 25])
print(python.total_minutes)
print(python.total_minutes)Bonnes pratiques
#- Une propriété doit s’exécuter rapidement et ne rien modifier. Le lecteur du code suppose que lire un attribut est une opération simple et sûre.
- Stockez la valeur dans un attribut au nom différent, généralement avec un underscore (
self._points). Écrireself.points = valueà l’intérieur du setter appellerait ce même setter indéfiniment. - Ne créez pas de propriétés par avance. Commencez par un attribut ordinaire et transformez-le en
@propertyquand un besoin de validation ou de calcul apparaît.
Entrées associées
#- DécorateursDes fonctions qui enveloppent d’autres fonctions pour leur ajouter un comportement, par ex. journalisation ou contrôle d’accès. Notation @nom.
- selfLe premier paramètre d’une méthode : l’objet sur lequel la méthode a été appelée. Il donne accès à ses attributs et à ses autres méthodes.
- @dataclassUn décorateur qui génère __init__, __repr__ et __eq__ à partir des champs annotés d’une classe qui sert à stocker des données.
Vous voyez une erreur ou il manque un exemple ? Écrivez-nous.