Property 데코레이터 심화
Coddy Python 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 64개 중 31번째.
고급 속성 데코레이터는 computed 속성, deleter 및 Full 속성 관리를 포함하여 attribute 액세스를 더욱 정교하게 제어할 수 있도록 합니다.
다음은 다른 attributes에서 값을 도출하는 Computed properties의 예입니다:
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)computed 속성을 일반 attributes처럼 사용하세요:
rect = Rectangle(5, 3)
print(rect.area) # 15 - 자동으로 계산됨
print(rect.perimeter) # 16 - 자동으로 계산됨getter, setter, deleter가 있는 property를 만드세요:
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전체 속성 functionality를 사용하세요:
temp = Temperature()유효성 검사를 사용하여 setter를 사용하세요:
temp.temperature = 25
print(temp.temperature) # 25
# temp.temperature = -300 # ValueError를 발생시킴deleter를 사용하세요:
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를 사용한 프로퍼티 삭제, 그리고 attributes의 가져오기, 설정 및 삭제에 대한 완전한 제어가 가능합니다. 이를 통해 강력한 데이터 검증과 캡슐화를 유지하면서 직관적인 인터페이스를 만들 수 있습니다.
챌린지
중급이 챌린지에서는 적절한 캡슐화와 속성 검증을 갖춘 Rectangle 클래스를 구현합니다.
rectangle.py- 편집해야 하는 파일이며, 구현을 안내하는 TODO 주석이 포함되어 있습니다driver.py- 광범위한 테스트 시나리오가 포함되어 있습니다(수정하지 마세요)
_width와_height에 대한 private attributes를 구현합니다width와height에 대한 속성을 생성하고 검증합니다(반드시 positive여야 함)- TODO에 지정된 대로 적절한 ValueError 메시지를 발생시킵니다
- 중요: 값이 0보다 작거나 같은지 확인한 다음 오류를 발생시킵니다(이렇게 해야 일관된 동작이 보장됩니다)
area와perimeter에 대한 읽기 전용 속성을 구현합니다dimensions속성을 생성하고 TODO에 설명된 getter, setter 및 deleter functionality를 구현합니다- 중요: setter에서는 튜플 언패킹 구문을 사용합니다:
width, height = dimensions
- 중요: setter에서는 튜플 언패킹 구문을 사용합니다:
직접 해보기
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}")
# Test the dimensions property
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}")
# deleter 테스트
del rect.dimensions
print(f"After reset: {rect.dimensions}")
# 0 값으로 테스트
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 # 속성 getter 접근
print(f"Final state after 1000 operations:")
print(f"Width: {rect.width}, Height: {rect.height}")
print(f"Area: {rect.area}, Perimeter: {rect.perimeter}")이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
직접 연습해 보세요: 온라인 Python 컴파일러