求助:如何实现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
关键说明
NameControl实现要点:
- 使用两个字典分别存储每个实例的字段值和初始化状态,避免类级别的数据共享;
__set__方法中检查实例是否已完成初始化,若已初始化则抛出对应错误;- 通过遍历类的属性映射,找到当前描述符绑定的字段名,输出精准的错误提示。
PriceControl修复:
- 原代码中
self.price是类变量,会导致所有Book实例共享同一个价格值,改用字典存储每个实例的独立价格,保证数据隔离。
- 原代码中
内容的提问来源于stack exchange,提问作者Jack Heathrow
相关产品推荐
相关产品推荐

