高级属性装饰器
本课属于 Coddy Python 学习旅程的“面向对象编程”单元。第 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()使用带验证的 setter:
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}") # 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 删除属性,还能全面控制属性的获取、设置和删除。这样既能创建直观的接口,又能保持严格的数据验证和封装。
挑战
中等在这个挑战中,你将实现一个 Rectangle 类,并正确进行封装和属性验证。
rectangle.py:这是你需要编辑的文件,其中包含用于指导你实现的 TODO 注释driver.py:包含大量测试场景(不要修改)
- 将值存储在受保护的(内部)属性
_width和_height中 - 为
width和height创建属性,并进行验证(值必须为正数)- 按照 TODO 中的说明,抛出适当的 ValueError 错误消息
- 重要:检查值是否小于或等于 0,如果是,则抛出错误(这样可以确保行为一致)
- 实现只读属性
area和perimeter - 按照 TODO 中的说明,为
dimensions创建具有获取器、设置器和删除器功能的属性- 重要:在设置器中使用元组解包语法:
width, height = dimensions
- 重要:在设置器中使用元组解包语法:
亲手试试
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 # 访问属性的 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 在线编译器