Reputation: 21
I need to make a descriptor that will turn some class attributes to immutable after initialization
I need to make 1 class and 2 descriptors that will manage its attributes. The task is:
You must implement the class Book with the attributes price, author, and name.
The author and name fields have to be immutable;
The price field may have changes but has to be in the 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.
Implement descriptors PriceControl and NameControl to validate parameters.*
I've done a code for basic class and the price descriptor but stuck with the author\name one. Can u please help me solve this code?
My code is:
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
return self.price
class NameControl:
"""
Descriptor which don't allow to change field value after initialization.
"""
pass
# def __set__(self, obj, value):
# if obj or value is None:
# return self
# elif obj is not None:
# raise ValueError(f'ValueError: Author can not be changed.')
# elif value is not None:
# raise ValueError(f'ValueError: Name can not be changed.')
class Book:
author = NameControl()
name = NameControl()
price = PriceControl()
def __init__(self, author, name, price):
vars(self)['author'] = author
self.name = name
self.price = price
Expected result is :
>>> 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.`
Upvotes: 1
Views: 92
Reputation: 5425
Here's an implementation of the Book
class along with the two descriptors:
Update: Modified PriceControl
and NameControl
as suggested by @chepner in the comments.
class PriceControl:
def __set_name__(self, owner, name):
self.name = "_" + name # Use a different name to store the value
def __get__(self, instance, owner):
return getattr(instance, self.name, None)
def __set__(self, instance, value):
if not (0 <= value <= 100):
raise ValueError("Price must be between 0 and 100")
setattr(instance, self.name, value)
class NameControl:
def __set_name__(self, owner, name):
self.name = "_" + name # Use a different name to store the value
def __get__(self, instance, owner):
return getattr(instance, self.name, None)
def __set__(self, instance, value):
if getattr(instance, self.name, None) is not None:
raise ValueError(f"'{self.name[1:]}' attribute is immutable")
setattr(instance, self.name, value)
class Book:
price = PriceControl()
author = NameControl()
name = NameControl()
def __init__(self, price, author, name):
self.price = price
self.author = author
self.name = name
def __repr__(self):
return f"Book(author={self.author}, name={self.name}, price={self.price})"
Code to demonstrate use:
# Creating a book instance
book1 = Book(price=50, author="Jane Doe", name="Book Title")
print(book1) # Output: Book(author=Jane Doe, name=Book Title, price=50)
# Changing the price (within range)
book1.price = 75
print(book1) # Output: Book(author=Jane Doe, name=Book Title, price=75)
# Attempting to change author (immutable attribute)
try:
book1.author = "John Smith"
except ValueError as e:
print(e) # Output: 'author' attribute is immutable
# Attempting to change name (immutable attribute)
try:
book1.name = "New Book Title"
except ValueError as e:
print(e) # Output: 'name' attribute is immutable
# Attempting to set an invalid price (out of range)
try:
book1.price = 200
except ValueError as e:
print(e) # Output: Price must be between 0 and 100
Output:
Book(author=Jane Doe, name=Book Title, price=50)
Book(author=Jane Doe, name=Book Title, price=75)
'author' attribute is immutable
'name' attribute is immutable
Price must be between 0 and 100
Upvotes: 2