Control how a book changes.
Encapsulation groups data with the rules that protect it. In Python, a leading underscore such as _year is a convention for internal use, not a strict access ban.
class Book:
def __init__(self, title, author, year):
self.title = title
self.author = author
self.is_available = True
self.year = year
@property
def year(self):
return self._year
@year.setter
def year(self, value):
if not isinstance(value, int) or value <= 0:
raise ValueError("year must be a positive integer")
self._year = value
def borrow(self):
self.is_available = False
def return_book(self):
self.is_available = True
book = Book("Dune", "Frank Herbert", 1965)
try:
book.year = 0
except ValueError:
pass
print(book.year)self.year = year calls the setter during creation, so validation also protects the initial value. The setter checks before assigning self._year.
1965The failed assignment raises ValueError before _year changes. After catching the error, the earlier valid year remains 1965.
Property pattern: Read book.year through the getter. Assign book.year = value through the setter. Keep _year as the internal stored value.
class Book:
def __init__(self, title, author, year):
self.title = title
self.author = author
self.is_available = True
self.year = year
@property
def year(self):
return self._year
@year.setter
def year(self, value):
if not isinstance(value, int) or value <= 0:
raise ValueError("year must be a positive integer")
self._year = value
def borrow(self):
self.is_available = False
def return_book(self):
self.is_available = True
book = Book("Dune", "Frank Herbert", 1965)
try:
book.year = 0
except ValueError:
pass
print(book.year)The public year property is easy to use, while its setter rejects zero, negative, or non-integer years.
@year.setter
def year(self, value):
if not isinstance(value, int) or value <= 0:
raise ValueError("year must be a positive integer")
self._year = valueValidate first and assign second. That order means invalid input cannot overwrite a valid stored value.
Follow a protected update
- 1. Use the public propertybook.year = 1965 calls the setter instead of writing directly to _year.
- 2. Validate the new valueThe setter accepts only positive integers and raises ValueError otherwise.
- 3. Store after validationOnly a valid value reaches self._year.
- 4. Keep the earlier stateA failed assignment leaves the book’s previously valid year unchanged.
Common mistakes
- Writing self.year inside the setterUse self._year in the setter; assigning self.year there calls the setter again.
- Assigning before validationCheck the value before changing _year so invalid state is never stored.
- Treating _year as private enforcementThe underscore is a convention that communicates internal use; Python does not block direct access.
- Forgetting validation in __init__Assign self.year = year in __init__ so creation uses the same setter rule.
