所以我的任务是:
*您必须实现具有价格、作者和名称属性的 Book 类。
作者和姓名字段必须是不可变的; 价格字段可能会发生变化,但必须在 0 <= price <= 100 range. If a user tries to change the author or name fields after an initialization or set price is out of range, the ValueError should be raised.
实现描述符 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
因此,正如您所看到的,我实现了价格控制的解决方案,但我无法想象如何为作者/名称控制创建描述符。你能帮我吗?
描述符不提供自动方式来精确地进行“一个”分配;您仍然需要定义一个 setter 来确定分配是否已经发生。 请注意,在这两种情况下,您应该将值附加到作为参数接收的
instance
,而不是描述符本身,否则
Book
的所有实例将共享相同的名称、作者和价格。# Hard-coded assumption that PriceControl will operate on
# an attribute named _price; see NameControl for a way to
# derive an attribute name from the name of the descriptor itself.
class PriceControl:
"""
Descriptor which don't allow to set price
less than 0 and more than 100 included.
"""
def __get__(self, instance, owner):
return instance._price
def __set__(self, instance, value):
if value < 0 or value > 100:
raise ValueError('Price must be between 0 and 100.')
instance._price = value
class NameControl:
# This method is invoked at class creation time,
# with the name of the class attribute bound to the
# descriptor instance passed as the 'name' argument.
# I.e.,
#
# class Book:
# author = NameControl()
#
# causes
#
# Book.author.__set_name__('author')
#
# to be called.
def __set_name__(self, name):
self.public_name = name
self.private_name = "_" + name
def __get__(self, instance, owner):
return getattr(instance, self.private_name)
def __set__(self, instance, value):
# TODO: check if instance already has the private
# instance attribute set below.
setattr(instance, self.private_name, value)
(您可能需要修改两个
__get__
方法以在
__set__
有机会创建底层实例属性之前处理它们的调用。)