88 lines
3.6 KiB
Python
88 lines
3.6 KiB
Python
# 第 3-4 课课堂练习:Python 特殊方法与数据类
|
||
#
|
||
# 关键规则已经写入每部分题目,可以直接完成练习。
|
||
# 请保留题目、预期结果和自查说明。
|
||
|
||
|
||
# 第一部分:定义 OrderItem 数据类
|
||
# 1. 在类上方添加 @dataclass。
|
||
# 2. 定义三个字段,不需要手写 __init__:
|
||
# product_name: str
|
||
# unit_price: float
|
||
# quantity: int = 1
|
||
#
|
||
# Java 对照提醒:
|
||
# - @dataclass 会生成 __init__、__repr__ 和 __eq__ 等常用方法;
|
||
# - 它类似减少 POJO 样板代码的工具,但不是 Java record 的完全等价物;
|
||
# - 数据类默认仍然可变,并不会自动进行运行时类型检查。
|
||
|
||
|
||
# 第二部分:使用 __post_init__ 校验字段
|
||
# 1. 定义 __post_init__(self) 方法。
|
||
# 2. unit_price 小于 0 时抛出 ValueError("单价不能小于 0。")。
|
||
# 3. quantity 小于或等于 0 时抛出 ValueError("数量必须大于 0。")。
|
||
#
|
||
# Java 对照提醒:
|
||
# - dataclass 自动生成 __init__ 后,会自动调用 __post_init__;
|
||
# - 类型注解只表达预期类型,不会阻止负数,所以业务校验仍需手写。
|
||
|
||
|
||
# 第三部分:定义 subtotal 只读 property
|
||
# 1. 使用 @property 定义 subtotal(self),返回单价乘数量。
|
||
# 2. 调用方式应为 item.subtotal,而不是 item.get_subtotal()。
|
||
|
||
|
||
# 第四部分:定义 __str__(self)
|
||
# 1. 返回“机械键盘 × 2:1000.0 元”格式的使用者友好文字。
|
||
# 2. 使用 self.subtotal 取得小计。
|
||
#
|
||
# 特殊方法提醒:
|
||
# - print(item) 和 str(item) 会调用 __str__;
|
||
# - repr(item) 会使用 dataclass 自动生成的 __repr__;
|
||
# - item_one == item_two 会使用自动生成的 __eq__ 比较全部字段。
|
||
|
||
|
||
# 第五部分:定义 ShoppingCart 数据类
|
||
# 1. 在类上方添加 @dataclass。
|
||
# 2. 定义 owner: str 字段。
|
||
# 3. 定义 items 字段:
|
||
# items: list[OrderItem] = field(default_factory=list)
|
||
# 4. 定义 add(self, item) 方法,把 item 添加到 self.items。
|
||
# 5. 定义 total 只读 property,返回所有 item.subtotal 的总和。
|
||
#
|
||
# 重要提醒:
|
||
# - 不要写 items = [] 作为类级默认列表;
|
||
# - default_factory=list 会为每个 ShoppingCart 对象创建独立列表;
|
||
# - 这与 Java 实例字段通常在构造时 new ArrayList<>() 的目的相似。
|
||
|
||
|
||
# 第六部分:定义 main() 并验证行为
|
||
# 1. 创建两个内容相同的 OrderItem("机械键盘", 500.0, 2)。
|
||
# 2. 输出第一个对象,预期调用 __str__。
|
||
# 3. 输出 repr(item_one),观察自动生成的调试表示。
|
||
# 4. 输出“两个订单项相等:True”。
|
||
# 5. 创建 ShoppingCart("小明") 和 ShoppingCart("小红")。
|
||
# 6. 只向小明的购物车添加第一个订单项。
|
||
# 7. 输出两个购物车的商品种类数,证明列表没有共享。
|
||
# 8. 输出小明购物车的总金额。
|
||
# 9. 使用 try...except 创建 OrderItem("错误商品", -1.0),捕获并输出错误。
|
||
# 10. 添加程序入口判断并调用 main()。
|
||
#
|
||
# 预期输出:
|
||
# 机械键盘 × 2:1000.0 元
|
||
# OrderItem(product_name='机械键盘', unit_price=500.0, quantity=2)
|
||
# 两个订单项相等:True
|
||
# 小明的商品种类数:1
|
||
# 小红的商品种类数:0
|
||
# 小明购物车总金额:1000.0 元
|
||
# 非法订单项已被阻止:单价不能小于 0。
|
||
|
||
|
||
# 完成后自查:
|
||
# 1. 是否没有为 OrderItem 手写 __init__、__repr__ 和 __eq__;
|
||
# 2. 是否能区分 __str__ 的用户显示与 __repr__ 的调试表示;
|
||
# 3. 是否知道 dataclass 的相等比较默认比较字段值;
|
||
# 4. 是否使用 __post_init__ 完成运行时业务校验;
|
||
# 5. 是否使用 default_factory=list 避免共享可变默认值;
|
||
# 6. 是否只把运行错误、结果错误和关键知识点错误视为阻塞项。
|