プロパティデコレータ(応用)
CoddyのPythonジャーニー「Object Oriented Programming」セクションの一部 — レッスン 31/64。
高度なプロパティデコレータは、計算済みプロパティ、デリータ、および完全なプロパティ管理を含む、属性アクセスに対するより洗練された制御を提供します。
他の属性から値を導出する算出プロパティの例を次に示します:
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)算出プロパティを通常の属性のように使用します:
rect = Rectangle(5, 3)
print(rect.area) # 15 - 自動的に計算されます
print(rect.perimeter) # 16 - 自動的に計算されますgetter、setter、および deleter を持つプロパティを作成します:
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 = 0プロパティの全機能を使用します:
temp = Temperature()バリデーション付きのセッターを使用します:
temp.temperature = 25
print(temp.temperature) # 25
# temp.temperature = -300 # ValueErrorが発生しますデリータを使用します:
del temp.temperature
print(temp.temperature) # 0ゲームのスコアを使用した、より複雑な例を作成します:
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}") # スコア: 2500、レベル: 3
del player.score
print(f"Score: {player.score}, Level: {player.level}") # スコア: 0、レベル: 1出力:
15
16
25
Resetting temperature to 0
0
Score: 2500, Level: 3
Resetting Alice's progress
Score: 0, Level: 1重要なポイント:高度なプロパティデコレータを使用すると、計算プロパティ(他のデータから計算されるもの)、@property.deleter によるプロパティの削除、および属性の取得、設定、削除の完全な制御が可能になります。これにより、強力なデータバリデーションとカプセル化を維持しながら、直感的なインターフェースを構築できます。
チャレンジ
中級このチャレンジでは、適切なカプセル化とプロパティのバリデーションを備えた Rectangle クラスを実装します。
rectangle.py- 編集が必要なファイルです。実装のガイドとなる TODO コメントが含まれています。driver.py- 広範なテストシナリオが含まれています(変更しないでください)。
_widthと_heightのプライベート属性を実装します。- バリデーション(正の値であること)を備えた
widthとheightのプロパティを作成します。- TODO で指定されている適切な ValueError メッセージを発生させます。
- 重要: 値が 0 以下であるかどうかを確認し、エラーを発生させます(これにより一貫した動作が保証されます)。
areaとperimeterの読み取り専用プロパティを実装します。- TODO で説明されているように、getter、setter、deleter 機能を備えた
dimensionsプロパティを作成します。- 重要: setter では、タプル展開の構文
width, height = dimensionsを使用してください。
- 重要: setter では、タプル展開の構文
チートシート
高度なプロパティデコレータは、計算済みプロパティ、デリータ、および完全なプロパティ管理により、属性アクセスに対する洗練された制御を提供します。
計算済みプロパティ:
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)ゲッター、セッター、およびデリータを備えた完全なプロパティ:
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 = 0使用法:
# 計算済みプロパティ
rect = Rectangle(5, 3)
print(rect.area) # 15
print(rect.perimeter) # 16
# 完全なプロパティ機能
temp = Temperature()
temp.temperature = 25 # セッターを使用
print(temp.temperature) # ゲッターを使用
del temp.temperature # デリータを使用自分で試してみよう
from rectangle import Rectangle
# テストケースのハンドラー
test_case = input()
# 基本機能のテスト
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}")
# dimensions プロパティのテスト
print(f"Dimensions: {rect.dimensions}")
rect.dimensions = (10, 8)
print(f"New area: {rect.area}")
# バリデーションのテスト
try:
rect.width = -2
except ValueError as e:
print(f"Validation error: {e}")
# デリーターのテスト
del rect.dimensions
print(f"After reset: {rect.dimensions}")
# ゼロ値でのテスト
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}")
# 負の値でのテスト
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}")
# 大きな値でのテスト
elif test_case == "large_values":
rect = Rectangle(1000000, 2000000)
print(f"Large rectangle area: {rect.area}")
print(f"Large rectangle perimeter: {rect.perimeter}")
# 浮動小数点値でのテスト
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}")
# 型エラーのテスト
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 # タプルではない
except Exception as e:
print(f"Type error setting dimensions: {type(e).__name__}: {e}")
# 複数のプロパティ操作のテスト
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}")
# プロパティバリデーションの境界値テスト
elif test_case == "validation_edge_cases":
rect = Rectangle(5, 3)
try:
rect.width = 0
except ValueError as e:
print(f"Zero width error: {e}")
# 非常に小さな正の値は受け入れられるべき
rect.height = 0.0001
print(f"Small height accepted: {rect.height}")
print(f"Area with small height: {rect.area}")
# 複数の長方形のテスト
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}")
# 各長方形を変更
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}")
# 多数の操作によるパフォーマンスのテスト
elif test_case == "performance_test":
rect = Rectangle(5, 5)
# 多数のプロパティ操作を実行
for i in range(1000):
rect.width = i % 10 + 1 # 1-10の値
rect.height = i % 5 + 1 # 1-5の値
area = rect.area # 計算されたプロパティにアクセス
perimeter = rect.perimeter # 計算されたプロパティにアクセス
dims = rect.dimensions # プロパティのゲッターにアクセス
print(f"Final state after 1000 operations:")
print(f"Width: {rect.width}, Height: {rect.height}")
print(f"Area: {rect.area}, Perimeter: {rect.perimeter}")このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。