Property 데코레이터 심화
Coddy Python 여정의 Object Oriented Programming 섹션에 포함된 레슨 — 64개 중 31번째.
고급 속성 데코레이터는 계산된 속성(computed properties), 삭제자(deleters) 및 전체 속성 관리를 포함하여 속성 접근에 대한 더 정교한 제어를 제공합니다.
다음은 다른 속성으로부터 값을 도출하는 계산된 속성(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)계산된 속성을 일반 속성처럼 사용하세요:
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를 발생시킵니다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}") # Score: 2500, Level: 3
del player.score
print(f"Score: {player.score}, Level: {player.level}") # Score: 0, Level: 1출력:
15
16
25
Resetting temperature to 0
0
Score: 2500, Level: 3
Resetting Alice's progress
Score: 0, Level: 1핵심 포인트: 고급 프로퍼티 데코레이터를 사용하면 계산된 프로퍼티(다른 데이터로부터 계산됨), @property.deleter를 사용한 프로퍼티 삭제, 그리고 속성의 가져오기(getting), 설정하기(setting), 삭제하기(deleting)에 대한 완전한 제어가 가능합니다. 이를 통해 강력한 데이터 검증 및 캡슐화를 유지하면서도 직관적인 인터페이스를 만들 수 있습니다.
챌린지
중급이 챌린지에서는 적절한 캡슐화와 속성 검증을 갖춘 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에서 튜플 언패킹 구문인
치트 시트
고급 프로퍼티 데코레이터는 계산된 프로퍼티(computed properties), 삭제자(deleters) 및 전체 프로퍼티 관리를 통해 속성 액세스에 대한 정교한 제어를 제공합니다.
계산된 프로퍼티(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)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사용법:
# 계산된 프로퍼티
rect = Rectangle(5, 3)
print(rect.area) # 15
print(rect.perimeter) # 16
# 전체 프로퍼티 기능
temp = Temperature()
temp.temperature = 25 # setter 사용
print(temp.temperature) # getter 사용
del temp.temperature # deleter 사용직접 해보기
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}")
# 삭제자(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}")이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.