Book represents a book while keeping its data and behavior readable. When construction logic becomes conditional, a @property method can calculate display_title without cluttering __init__ or adding a setter. section 9.3.1
As author behavior grows, move it into an Author class. Book can still expose author_for_display and author_for_citation by forwarding those calls to the new object, preserving existing consumers while separating concerns. section 9.3.2
Forward author formatting through Book
class Author: @property def for_display(self): return f'{self.first_name} {self.last_name}' class Book: @property def author_for_display(self): return self.author.for_display section 9.3.2
Listing 9.8. Using @property to simplify class construction
class Book:
def __init__(self, data):
self.title = data['title']
self.subtitle = data['subtitle']
@property
def display_title(self): #1
if self.title and self.subtitle:
return f'{self.title}: {self.subtitle}'
elif self.title:
return self.title
else:
return 'Untitled'
Compare direct initialization logic with a @property that computes display_title when accessed. section 9.3.1
Listing 9.10. Extracting an Author class from the Book class
class Author:
def __init__(self, author_data): #1
self.first_name = author_data['first_name']
self.last_name = author_data['last_name']
@property
def for_display(self): #2
return f'{self.first_name} {self.last_name}'
@property
def for_citation(self):
return f'{self.last_name}, {self.first_name[0]}.'
class Book:
def __init__(self, data):
# ...
self.author_data = data['author'] #3
self.author = Author(self.author_data) #4
@property
def author_for_display(self): #5
return self.author.for_display
@property
def author_for_citation(self):
return self.author.for_citation
Inspect the complete extraction of Author and the compatibility properties retained on Book. section 9.3.2
