Refactoring From Messy to Idiomatic Writing code that works is only the first step. True software craftsmanship lies in making that code readable, maintainable, and idiomatic. In Python, we call this writing Pythonic code. Writing Pythonic code means using the language's built-in strengths rather than fighting against them. We want to avoid overengineering, reduce boilerplate, and write logic that feels natural. This guide steps through how to refactor a clunky, rigid fitness tracking script into clean, beautiful Python. We will replace unnecessary classes with clean functions, implement safe resource management, and apply modern standard library tools. Prerequisites and Toolkit To follow this tutorial, you should understand basic Python syntax, functions, and file operations. We will use several built-in modules to clean up our code: * dataclasses: Simplifies class creation for data storage. * pathlib: Provides an object-oriented approach to handling file paths. * logging: Offers a robust way to track events instead of using print statements. The Code Walkthrough Let us look at how to structure our program. We start by removing a class that has no state and replacing it with focused functions. Then, we use a data class to structure our inputs and an iterator to stream data safely. ```python from dataclasses import dataclass, field from datetime import datetime from collections.abc import Iterator from pathlib import Path import logging Centralize paths as constants using Path objects FOOD_FILE = Path("food.csv") ACTIVITY_FILE = Path("activities.csv") Setup logging configuration logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" ) def today() -> str: return datetime.now().strftime("%Y-%m-%d") @dataclass class Entry: description: str calories: int date: str = field(default_factory=today) def append_entry(file_path: Path, entry: Entry) -> None: # Using a context manager ensures safe file closing with open(file_path, "a") as f: f.write(f"{entry.date},{entry.description},{entry.calories}\n") logging.info(f"Appended entry: {entry.description} ({entry.calories} cal)") def read_entries(file_path: Path) -> Iterator[Entry]: # Easier to Ask Forgiveness than Permission (EAFP) try: with open(file_path, "r") as f: for line in f: parts = line.strip().split(",") if len(parts) == 3: yield Entry(parts[1], int(parts[2]), parts[0]) except FileNotFoundError: logging.warning(f"{file_path} not found. Starting fresh.") def run_day_summary(date: str) -> None: food_list = list(read_entries(FOOD_FILE)) activity_list = list(read_entries(ACTIVITY_FILE)) # List comprehensions filter and aggregate data cleanly food_total = sum(e.calories for e in food_list if e.date == date) activity_total = sum(e.calories for e in activity_list if e.date == date) net_calories = food_total - activity_total print(f"--- Summary for {date} ---") print(f"Food intake: {food_total} calories") print(f"Activity burn: {activity_total} calories") print(f"Net: {net_calories} calories") def main() -> None: # Clear entry point avoiding global namespace pollution banana = Entry(description="banana", calories=100) append_entry(FOOD_FILE, banana) running = Entry(description="running", calories=300) append_entry(ACTIVITY_FILE, running) run_day_summary(today()) if __name__ == "__main__": main() ``` This refactored script is modular. We replaced manual file closing with context managers. We replaced repetitive manual parsing with a reusable generator function that yields clean data objects. Syntax and Best Practices This refactoring highlights several Pythonic paradigms. First, we favor **EAFP** (Easier to Ask Forgiveness than Permission) over **LBYL** (Look Before You Leap). Instead of checking if a file exists before opening it, we try to read it and catch the `FileNotFoundError`. Second, we use **default factories** in our data class. If you write `date: str = today()`, Python evaluates that default value exactly once when it loads the module. Every entry created thereafter gets that same frozen timestamp. By using `default_factory=today`, Python executes our function every single time we instantiate a new `Entry`. Finally, we use an **Iterator** via the `yield` keyword. Yielding records one by one keeps memory consumption minimal, even when handling large files. Tips and Pitfalls Avoid creating classes that do not hold state. If a class only contains a constructor and a few methods that do not modify `self`, delete the class. Write pure functions instead. Do not use print statements for operational tracking. Prints clutter standard output and make debugging difficult. Standardize on the built-in logging library to gain timestamps and severity levels automatically.
logging
Libraries
Nov 2022 • 1 videos
High activity month for logging. ArjanCodes among the most active voices, with 1 videos across 1 sources.
Nov 2022
Nov 2025 • 1 videos
High activity month for logging. ArjanCodes among the most active voices, with 1 videos across 1 sources.
Nov 2025
- Nov 7, 2025
- Nov 18, 2022