-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
105: Practice Exercise: Building a Simple File-Based Journal App
Alright, it's time to actually build something. We've talked about variables, loops, and file I/O, but putting them together in a real project is where the "aha!" moments usually happen. We're going to build a simple journal app. The goal is basic: the user writes a thought, and we save it to a file so it's still there when they restart the program.
The "Just Append It" Trap
If you were just hacking this together for a quick prototype, your first instinct might be to just open a text file in append mode and dump the user's string right into it. It looks something like this:
def save_entry(text):
f = open("journal.txt", "a")
f.write(text + "\n")
f.close()
On the surface, this works. You run the script, you type "Today I learned about Python," and it's in the file. But as a professional, this is the kind of code that makes me nervous during a code review. Why? Because it's fragile. If the program crashes or the power cuts out right after open() but before close(), you risk corrupting the file. Plus, you're just dumping raw text into a void. If you want to go back and find an entry from last Tuesday, you're stuck reading the whole file and guessing where one entry ends and the next begins.
When the Data Becomes a Mess
The real problem with the naive approach is that it treats data as a stream of characters rather than a collection of objects. Imagine your journal grows to 1,000 entries. If you want to implement a feature to "Delete entry #42" or "Search for all entries containing 'Python'," you're going to have a nightmare of a time parsing a plain .txt file. You'd have to read the entire file into memory, split it by newlines, and hope the user didn't happen to press "Enter" inside their journal entry, which would break your entire parsing logic.
Structuring for the Future
The better way—and the way I'd want to see this implemented—is to use a context manager and a structured data format like JSON. Using with open(...) ensures the file closes itself even if an exception is raised, which saves you from those weird "file in use" errors on Windows. More importantly, by storing entries as a list of dictionaries, we can attach metadata—like a timestamp—to every single note.
Here is how I would approach the core logic:
import json
from datetime import datetime
def save_entry(text):
# We load the existing entries first so we can append to the list
try:
with open("journal.json", "r") as f:
entries = json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
entries = []
# Create a structured object for the entry
new_entry = {
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"content": text
}
entries.append(new_entry)
# Write the whole list back to the file
with open("journal.json", "w") as f:
json.dump(entries, f, indent=4)
Now, we've traded a tiny bit of performance (since we're reading the whole file before writing) for an immense gain in reliability and flexibility. If we decide later that we want to add "Tags" or "Mood" to each journal entry, we just add another key to the dictionary. The structure stays intact, and our data remains clean.
📋 Practical Task
Project: Building a Timestamped Journal with JSON Persistence
Your task is to build a fully functional command-line Journal Application. Instead of just a single save function, you need to create a program that runs in a loop and gives the user a menu of options.
Requirements:
- The Main Loop: The program should stay open until the user types 'quit'.
- Adding Entries: Allow the user to input a journal entry. Save it to a file named
my_journal.jsonusing the structured approach (JSON) discussed in the lesson, including a timestamp for each entry. - Viewing Entries: Create an option that reads the JSON file and prints all past entries in a clean format (e.g.,
[2023-10-27 14:00] - This was a great day!). - Error Handling: Ensure the program doesn't crash if
my_journal.jsondoesn't exist yet when the user tries to view their entries for the first time.
There are no comments for now.