Décorateurs Property Avancés
Fait partie de la section Programmation orientée objet du Journey Python de Coddy. Leçon 31 sur 64.
Les décorateurs de propriétés avancés offrent un contrôle plus sophistiqué de l’accès aux attributs, notamment les propriétés calculées, les suppresseurs et une gestion complète des propriétés.
Voici un exemple de propriétés calculées qui dérivent des valeurs à partir d’autres attributs :
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
@property
def area(self):
return self.width * self.height
@property
def perimeter(self):
return 2 * (self.width + self.height)Utilisez les propriétés calculées comme des attributs ordinaires :
rect = Rectangle(5, 3)
print(rect.area) # 15 - calculé automatiquement
print(rect.perimeter) # 16 - calculé automatiquementCréez une propriété avec un accesseur, un mutateur et un suppresseur :
class Temperature:
def __init__(self):
self._temp = 0
@property
def temperature(self):
return self._temp
@temperature.setter
def temperature(self, value):
if value < -273.15:
raise ValueError("Temperature below absolute zero!")
self._temp = value
@temperature.deleter
def temperature(self):
print("Resetting temperature to 0")
self._temp = 0Utilisez toute la fonctionnalité de propriété :
temp = Temperature()Utilisez le setter avec validation :
temp.temperature = 25
print(temp.temperature) # 25
# temp.temperature = -300 # Lèverait ValueErrorUtilisez le suppresseur :
del temp.temperature
print(temp.temperature) # 0Créez un exemple plus complexe avec un score de jeu :
class Player:
def __init__(self, name):
self.name = name
self._score = 0
self._level = 1
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if value >= 0:
self._score = value
self._level = (value // 1000) + 1
else:
raise ValueError("Score cannot be negative")
@score.deleter
def score(self):
print(f"Resetting {self.name}'s progress")
self._score = 0
self._level = 1
@property
def level(self):
return self._level
player = Player("Alice")
player.score = 2500
print(f"Score: {player.score}, Level: {player.level}") # Score: 2500, Level: 3
del player.score
print(f"Score: {player.score}, Level: {player.level}") # Score: 0, Level: 1Sortie :
15
16
25
Resetting temperature to 0
0
Score: 2500, Level: 3
Resetting Alice's progress
Score: 0, Level: 1Point clé : les décorateurs de propriété avancés permettent de créer des propriétés calculées (à partir d’autres données), de supprimer des propriétés avec @property.deleter et de contrôler entièrement la lecture, la modification et la suppression des attributs. Cela permet de créer des interfaces intuitives tout en assurant une validation rigoureuse des données et leur encapsulation.
Défi
MoyenDans ce défi, vous allez implémenter une classe Rectangle avec un encapsulage approprié et une validation des propriétés.
rectangle.py- Voici le fichier à modifier, qui contient des commentaires TODO pour vous guider dans votre implémentationdriver.py- Contient de nombreux scénarios de test (ne pas modifier)
- Stockez les valeurs dans les attributs protégés (internes)
_widthet_height - Créez des propriétés pour
widthetheightavec validation (elles doivent être positives)- Levez les exceptions ValueError appropriées, comme indiqué dans les TODO
- Important : Vérifiez si la valeur est inférieure ou égale à 0, puis levez l’erreur (cela garantit un comportement cohérent)
- Implémentez des propriétés en lecture seule pour
areaetperimeter - Créez une propriété
dimensionsavec les fonctionnalités getter, setter et deleter décrites dans les TODO- Important : Dans le setter, utilisez la syntaxe de déballage de tuple :
width, height = dimensions
- Important : Dans le setter, utilisez la syntaxe de déballage de tuple :
Essayez vous-même
from rectangle import Rectangle
# Gestionnaire des cas de test
test_case = input()
# Test des fonctionnalités de base
if test_case == "default_test":
rect = Rectangle(5, 3)
print(f"Width: {rect.width}, Height: {rect.height}")
print(f"Area: {rect.area}, Perimeter: {rect.perimeter}")
# Tester la propriété dimensions
print(f"Dimensions: {rect.dimensions}")
rect.dimensions = (10, 8)
print(f"New area: {rect.area}")
# Tester la validation
try:
rect.width = -2
except ValueError as e:
print(f"Validation error: {e}")
# Tester la suppression de la propriété
del rect.dimensions
print(f"After reset: {rect.dimensions}")
# Test avec des valeurs nulles
elif test_case == "zero_values":
try:
rect = Rectangle(0, 5)
except ValueError as e:
print(f"Error creating rectangle: {e}")
try:
rect = Rectangle(5, 0)
except ValueError as e:
print(f"Error creating rectangle: {e}")
# Test avec des valeurs négatives
elif test_case == "negative_values":
rect = Rectangle(5, 3)
original_dimensions = rect.dimensions
try:
rect.dimensions = (5, -3)
except ValueError as e:
print(f"Error setting dimensions: {e}")
print(f"Dimensions after failed update: {rect.dimensions}")
print(f"Original dimensions preserved: {rect.dimensions == original_dimensions}")
# Test avec de grandes valeurs
elif test_case == "large_values":
rect = Rectangle(1000000, 2000000)
print(f"Large rectangle area: {rect.area}")
print(f"Large rectangle perimeter: {rect.perimeter}")
# Test avec des valeurs à virgule flottante
elif test_case == "float_values":
rect = Rectangle(3.5, 2.75)
print(f"Dimensions: {rect.dimensions}")
print(f"Area: {rect.area}")
print(f"Perimeter: {rect.perimeter}")
rect.dimensions = (1.1, 2.2)
print(f"New area with float dimensions: {rect.area}")
# Test avec des erreurs de type
elif test_case == "type_errors":
try:
rect = Rectangle("5", 3)
except Exception as e:
print(f"Type error during creation: {type(e).__name__}: {e}")
rect = Rectangle(5, 3)
try:
rect.dimensions = 10 # Pas un tuple
except Exception as e:
print(f"Type error setting dimensions: {type(e).__name__}: {e}")
# Tester plusieurs opérations sur les propriétés
elif test_case == "property_operations":
rect = Rectangle(5, 10)
print(f"Initial - Width: {rect.width}, Height: {rect.height}, Area: {rect.area}")
rect.width = 8
print(f"After width change - Width: {rect.width}, Area: {rect.area}")
rect.height = 6
print(f"After height change - Height: {rect.height}, Area: {rect.area}")
rect.dimensions = (12, 9)
print(f"After dimensions change - Dimensions: {rect.dimensions}, Area: {rect.area}")
del rect.dimensions
print(f"After reset - Dimensions: {rect.dimensions}, Area: {rect.area}")
# Tester les cas limites de validation des propriétés
elif test_case == "validation_edge_cases":
rect = Rectangle(5, 3)
try:
rect.width = 0
except ValueError as e:
print(f"Zero width error: {e}")
# Une très petite valeur positive doit être acceptée
rect.height = 0.0001
print(f"Small height accepted: {rect.height}")
print(f"Area with small height: {rect.area}")
# Tester plusieurs rectangles
elif test_case == "multiple_rectangles":
rect1 = Rectangle(5, 3)
rect2 = Rectangle(10, 2)
rect3 = Rectangle(4, 4)
print(f"Rectangle 1 - Area: {rect1.area}, Perimeter: {rect1.perimeter}")
print(f"Rectangle 2 - Area: {rect2.area}, Perimeter: {rect2.perimeter}")
print(f"Rectangle 3 - Area: {rect3.area}, Perimeter: {rect3.perimeter}")
# Modifier chaque rectangle
rect1.width = 7
rect2.height = 5
rect3.dimensions = (6, 6)
print(f"After modifications:")
print(f"Rectangle 1 - Dimensions: {rect1.dimensions}")
print(f"Rectangle 2 - Dimensions: {rect2.dimensions}")
print(f"Rectangle 3 - Dimensions: {rect3.dimensions}")
# Tester les performances avec de nombreuses opérations
elif test_case == "performance_test":
rect = Rectangle(5, 5)
# Effectuer de nombreuses opérations sur les propriétés
for i in range(1000):
rect.width = i % 10 + 1 # Valeurs 1-10
rect.height = i % 5 + 1 # Valeurs 1-5
area = rect.area # Accéder à la propriété calculée
perimeter = rect.perimeter # Accéder à la propriété calculée
dims = rect.dimensions # Accéder à l’accesseur de la propriété
print(f"Final state after 1000 operations:")
print(f"Width: {rect.width}, Height: {rect.height}")
print(f"Area: {rect.area}, Perimeter: {rect.perimeter}")Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation orientée objet
1Les fondamentaux de la POO
Fichiers externesIntroduction à la POOClasses vs ObjetsLe paramètre selfMéthodesAttributsMéthode constructeur (__init__)Récapitulatif - Calculatrice simple4Héritage
Héritage de baseLa fonction super()Redéfinition de méthodeHéritage multipleOrdre de résolution des méthodesRécapitulatif - Hiérarchie des employés7Méthodes spéciales
Introduction aux méthodes magiquesSurcharge d'opérateursMéthodes magiques de conteneurRécapitulatif - Liste personnalisée10Patrons de conception, Partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FactoryPatron ObserverPatron Strategy2Décorateurs
Introduction aux décorateursDécorateur de propriétéDécorateur de méthode statiqueDécorateur de méthode de classe5Polymorphisme
Retour sur la redéfinition de méthodeDuck TypingClasses et méthodes abstraitesConception d'interfacesRécapitulatif - Calculateur de formes8Concepts avancés de la POO
Composition vs HéritageMixinsMéthodes statiques et de classeDécorateurs de classeGestionnaires de contexte11Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode TemplatePatron ÉtatPatron Composite3Propriétés de classe
Variables d'instance vs variables de classeDécorateurs de propriétéAttributs privésRécapitulatif - Gestionnaire de compte bancaire6Encapsulation
Membres Publics, Protégés, PrivésModificateurs d'accèsMasquage d'informationsDécorateurs Property AvancésRécapitulatif - Système de Gestion d'ÉtudiantsEntraînez-vous par vous-même : Compilateur Python en ligne