You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:如何实现NameControl描述符限制Book类的author/name不可变?

问题:实现Book类的不可变字段与价格验证描述符

任务要求

  • 实现包含price、author、name属性的Book类,满足:
    • author和name为不可变字段;
    • price可修改,但必须符合0 ≤ price ≤ 100;
    • 初始化后修改author/name,或设置price超出范围时抛出ValueError;
    • 必须通过PriceControl和NameControl两个描述符实现参数验证。

示例运行效果

>>> b = Book("William Faulkner", "The Sound and the Fury", 12)
>>> print(f"Author='{b.author}', Name='{b.name}', Price='{b.price}'")
Author='William Faulkner', Name='The Sound and the Fury', Price='12'

>>> b.price = 55
>>> b.price
55
>>> b.price = -12  # => ValueError: Price must be between 0 and 100.
>>> b.price = 101  # => ValueError: Price must be between 0 and 100.

>>> b.author = "new author"  # => ValueError: Author can not be changed.
>>> b.name = "new name"      # => ValueError: Name can not be changed.

当前实现代码

class PriceControl:
    """
    Descriptor which don't allow to set price
    less than 0 and more than 100 included.
    """
    def __init__(self):
        self.price = 0

    def __get__(self, instance, owner):
        return self.price
    def __set__(self, instance, value):
        if value < 0 or value > 100:
            raise ValueError('Price must be between 0 and 100.')
        self.price = value

class NameControl:
    """
    Descriptor which don't allow to change field value after initialization.
    """
    pass

class Book:
    author = NameControl()
    name = NameControl()
    price = PriceControl()
    def __init__(self, author, name, price):
        self.author = author
        self.name = name
        self.price = price

解决方案

完整实现代码

class PriceControl:
    """
    Descriptor which don't allow to set price
    less than 0 and more than 100 included.
    """
    def __init__(self):
        self._prices = {}

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return self._prices.get(instance, 0)
    
    def __set__(self, instance, value):
        if not (0 <= value <= 100):
            raise ValueError('Price must be between 0 and 100.')
        self._prices[instance] = value

class NameControl:
    """
    Descriptor which don't allow to change field value after initialization.
    """
    def __init__(self):
        self._values = {}
        self._initialized = {}

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return self._values.get(instance)
    
    def __set__(self, instance, value):
        if self._initialized.get(instance, False):
            # 匹配当前描述符绑定的属性名,生成精准错误信息
            for attr_name, attr in owner.__dict__.items():
                if attr is self:
                    raise ValueError(f'{attr_name.capitalize()} can not be changed.')
            raise ValueError('Field can not be changed.')
        self._values[instance] = value
        self._initialized[instance] = True

class Book:
    author = NameControl()
    name = NameControl()
    price = PriceControl()
    def __init__(self, author, name, price):
        self.author = author
        self.name = name
        self.price = price

关键说明

  1. NameControl实现要点:

    • 使用两个字典分别存储每个实例的字段值和初始化状态,避免类级别的数据共享;
    • __set__方法中检查实例是否已完成初始化,若已初始化则抛出对应错误;
    • 通过遍历类的属性映射,找到当前描述符绑定的字段名,输出精准的错误提示。
  2. PriceControl修复:

    • 原代码中self.price是类变量,会导致所有Book实例共享同一个价格值,改用字典存储每个实例的独立价格,保证数据隔离。

内容的提问来源于stack exchange,提问作者Jack Heathrow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 23:00:39