Chapter 61: Decorators, Properties, and Advanced Patterns
James opens the Note class from Chapter 60. It displays itself, compares itself, iterates, hashes. It is Pythonic. But one thing bothers him: word_count is a stored field. Every time he edits the body, the count goes stale. He changed the body last session and forgot to update word_count. The object lied to him.
"The count should compute itself," Emma says. "You should not have to remember to update it. That is what @property does: it makes a method look like an attribute. You access note.word_count, and Python runs the computation behind the scenes."
This chapter teaches the final set of OOP patterns for Phase 5. You will add @property for computed attributes, @staticmethod and @classmethod for organizing class functionality, custom decorators for cross-cutting behavior, and Protocol for defining interfaces without inheritance. Every concept connects to the SmartNotes project, and every lesson uses the same TDG cycle you mastered in Phase 4.
What You Will Learn
By the end of this chapter, you will be able to:
- Use
@propertyto replace stored fields with computed values that stay synchronized with the object's state - Choose between
@staticmethod(utility functions) and@classmethod(alternative constructors) and place each appropriately in a class - Write custom decorators that modify function behavior, and use
functools.wrapsto preserve function metadata - Define a
Protocolfor structural subtyping and explain how it differs from ABC for dependency injection and testing - Combine all Phase 5 patterns in a capstone TDG cycle: Repository Protocol,
@validate_inputdecorator, computed properties, and factory methods
Chapter Lessons
| Lesson | Title | What You Do | Duration |
|---|---|---|---|
| 1 | Properties: Computed Attributes | Replace stored word_count with @property that computes from self.body. Learn getters, setters, and why properties keep interfaces stable. | 20 min |
| 2 | Static Methods and Class Methods | Move note_from_dict() onto the Note class as Note.from_dict() using @classmethod. Organize utility functions with @staticmethod. | 15 min |
| 3 | Custom Decorators | Write @log_calls and @validate_input decorators. Understand that @decorator is func = decorator(func). Use functools.wraps. | 20 min |
| 4 | Protocols: Structural Subtyping | Define a Repository Protocol with save() and find_by_id(). Write InMemoryRepository that satisfies the Protocol without inheriting from it. Compare Protocol vs ABC. | 20 min |
| 5 | SmartNotes Advanced Patterns Capstone | Combine all Phase 5 patterns in one TDG cycle: Repository Protocol, @validate_input, @property, Note.from_dict(). All tests pass. | 25 min |
| 6 | Chapter 61 Quiz | 20 scenario-based questions covering all five lessons | 25 min |
PRIMM-AI+ in This Chapter
Every lesson includes a PRIMM-AI+ Practice section following the five-stage cycle from Chapter 42. This is Phase 5: the TDG cycle now specifies class interfaces with decorators, properties, and protocols.
| Stage | What You Do | Claude Code Tool |
|---|---|---|
| Predict [AI-FREE] | Predict what @property returns, what @classmethod receives as its first argument, or whether a class satisfies a Protocol. Press Shift+Tab for Plan Mode. | Plan Mode protects prediction |
| Run | Press Shift+Tab to exit Plan Mode. Run uv run pyright on class stubs, uv run pytest on method tests, or uv run python to check property behavior. Compare results to your prediction. | You run it, not Claude Code |
| Investigate | Read test output or property behavior to understand why a prediction was wrong. In Claude Code, type: /investigate for Socratic questions about decorators and protocols. | /investigate probes mechanics |
| Modify | Refine a property, fix a decorator, or update a class to satisfy a Protocol. Predict whether the change resolves the issue. | You edit, Claude Code reviews |
| Make [Mastery Gate] | Build the SmartNotes advanced architecture with all patterns using the TDG cycle. In Claude Code, type: /tdg for structured TDG workflows. | Your work, Claude Code guides |
Your PRIMM-AI+ starter kit from Chapter 42 works throughout Phase 5. Use /predict, /investigate, /bug, /debug, and /tdg commands as you work through each lesson.
Prerequisites
Before starting this chapter, you should be able to:
- Build a Python class with
__init__, typed methods, and instance attributes (Chapter 58) - Choose between inheritance and composition using the design decision framework (Chapter 59)
- Implement special methods (
__repr__,__eq__,__iter__,__len__,__contains__,__hash__) for Pythonic objects (Chapter 60) - Drive the full TDG cycle independently: specify, test, generate, verify, debug (Chapter 57)
The SmartNotes Connection
This chapter completes the SmartNotes object model. The Note class gains computed properties (word_count from @property), a factory method (Note.from_dict() via @classmethod), input validation (@validate_input decorator), and a testable storage interface (Repository Protocol with InMemoryRepository).
The five lessons build the final architecture:
- Lesson 1 (Computation): Replace the stale
word_countfield with a live@property - Lesson 2 (Organization): Add
Note.from_dict()as a@classmethodfactory and organize utilities with@staticmethod - Lesson 3 (Cross-Cutting): Write
@validate_inputto guard methods across the class - Lesson 4 (Interfaces): Define a
RepositoryProtocol so storage can be swapped without changing the class hierarchy - Lesson 5 (Capstone): Combine everything in one TDG cycle with full test coverage