Pythonic Code & Best Practices

Writing Code the Python Way

What is "Pythonic" Code?

You can solve any problem in many ways. But Python has a preferred style — clean, readable, and elegant. Code written this way is called Pythonic.

Two programmers can solve the same problem:


    # Non-Pythonic — works but ugly
    numbers = [1, 2, 3, 4, 5]
    squares = []
    for i in range(len(numbers)):
        squares.append(numbers[i] ** 2)

    # Pythonic — clean and elegant
    squares = [n ** 2 for n in numbers]

Same result. But the second version is shorter, more readable, and more Python-like.

This stage teaches you to write code like an experienced Python developer.


Part 1 — List Comprehensions

The Problem

This pattern appears everywhere:


    # Create a new list by transforming another list
    result = []
    for item in original_list:
        result.append(transform(item))

It works but it's 3 lines every time. List comprehension does it in 1.


Basic List Comprehension


    # Syntax
    new_list = [expression for item in iterable]

Examples:


    # Squares of numbers
    numbers = [1, 2, 3, 4, 5]

    # Old way
    squares = []
    for n in numbers:
        squares.append(n ** 2)

    # List comprehension
    squares = [n ** 2 for n in numbers]
    print(squares)    # [1, 4, 9, 16, 25]

    # Uppercase all strings
    fruits = ["apple", "banana", "mango"]
    upper_fruits = [f.upper() for f in fruits]
    print(upper_fruits)    # ['APPLE', 'BANANA', 'MANGO']

    # Get lengths of words
    words = ["Python", "is", "awesome"]
    lengths = [len(w) for w in words]
    print(lengths)    # [6, 2, 7]


List Comprehension with Condition


    # Syntax
    new_list = [expression for item in iterable if condition]
    numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    # Only even numbers
    evens = [n for n in numbers if n % 2 == 0]
    print(evens)    # [2, 4, 6, 8, 10]

    # Only numbers greater than 5
    big = [n for n in numbers if n > 5]
    print(big)    # [6, 7, 8, 9, 10]

    # Squares of only even numbers
    even_squares = [n ** 2 for n in numbers if n % 2 == 0]
    print(even_squares)    # [4, 16, 36, 64, 100]


Dictionary Comprehension

Same concept for dictionaries:


    # Syntax
    new_dict = {key: value for item in iterable}

    # Square of each number as dict
    numbers = [1, 2, 3, 4, 5]
    squares_dict = {n: n ** 2 for n in numbers}
    print(squares_dict)    # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

    # Flip keys and values
    original = {"name": "Gagan", "age": 22, "city": "Delhi"}
    flipped = {value: key for key, value in original.items()}
    print(flipped)    # {'Gagan': 'name', 22: 'age', 'Delhi': 'city'}

    # Filter a dictionary
    marks = {"Rahul": 85, "Priya": 92, "Gagan": 45, "Amit": 78, "Neha": 38}
    passed = {name: mark for name, mark in marks.items() if mark >= 50}
    print(passed)    # {'Rahul': 85, 'Priya': 92, 'Amit': 78}


Set Comprehension


    numbers = [1, 2, 2, 3, 3, 3, 4]
    unique_squares = {n ** 2 for n in numbers}
    print(unique_squares)    # {1, 4, 9, 16} — unique values only


When NOT to Use Comprehensions

Comprehensions are great but don't overuse them:


    # Too complex — hard to read
    result = [x ** 2 for x in range(20) if x % 2 == 0 if x % 3 == 0]

    # Better as a regular loop with clear logic
    result = []
    for x in range(20):
        if x % 2 == 0 and x % 3 == 0:
            result.append(x ** 2)

Rule — if comprehension is hard to read in one glance, use a regular loop.


Part 2 — Lambda Functions

What is a Lambda?

A lambda is a small anonymous function — a function without a name, written in one line.


    # Regular function
    def square(n):
        return n ** 2

    # Lambda equivalent
    square = lambda n: n ** 2

    print(square(5))    # 25

Syntax:

lambda parameters: expression

Why Lambda?

Lambdas are not meant to replace regular functions. They're useful when you need a small throwaway function in one place — especially with sorted(), map(), filter().


Lambda with sorted()


    students = [
        {"name": "Rahul", "marks": 85},
        {"name": "Priya", "marks": 92},
        {"name": "Gagan", "marks": 78},
        {"name": "Amit", "marks": 95}
    ]

    # Sort by marks
    sorted_students = sorted(students, key=lambda s: s["marks"])
    for s in sorted_students:
        print(f"{s['name']}: {s['marks']}")

Output:

Gagan: 78
Rahul: 85
Priya: 92
Amit: 95

    # Sort by name
    sorted_by_name = sorted(students, key=lambda s: s["name"])

    # Sort by marks descending
    sorted_desc = sorted(students, key=lambda s: s["marks"], reverse=True)


map() — Apply Function to Every Item


    numbers = [1, 2, 3, 4, 5]

    # Apply function to each item
    squares = list(map(lambda n: n ** 2, numbers))
    print(squares)    # [1, 4, 9, 16, 25]

    # Same as list comprehension
    squares = [n ** 2 for n in numbers]

    # Convert all strings to uppercase
    names = ["rahul", "priya", "gagan"]
    upper = list(map(lambda n: n.upper(), names))
    print(upper)    # ['RAHUL', 'PRIYA', 'GAGAN']


filter() — Filter Items by Condition


    numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    # Keep only even numbers
    evens = list(filter(lambda n: n % 2 == 0, numbers))
    print(evens)    # [2, 4, 6, 8, 10]

    # Keep only passing marks
    marks = [85, 42, 90, 38, 75, 55, 29]
    passed = list(filter(lambda m: m >= 50, marks))
    print(passed)    # [85, 90, 75, 55]


Part 3 — Useful Pythonic Patterns

Unpacking


    # Basic unpacking
    a, b, c = [1, 2, 3]
    print(a, b, c)    # 1 2 3

    # Swap variables — Pythonic way
    x, y = 10, 20
    x, y = y, x       # swap in one line
    print(x, y)       # 20 10

    # Extended unpacking
    first, *rest = [1, 2, 3, 4, 5]
    print(first)    # 1
    print(rest)     # [2, 3, 4, 5]

    *beginning, last = [1, 2, 3, 4, 5]
    print(beginning)    # [1, 2, 3, 4]
    print(last)         # 5


Enumerate — Already Seen, Important Enough to Revisit


    fruits = ["apple", "banana", "mango"]

    # Non-Pythonic
    for i in range(len(fruits)):
        print(f"{i + 1}. {fruits[i]}")

    # Pythonic
    for i, fruit in enumerate(fruits, start=1):
        print(f"{i}. {fruit}")

Output:

1. apple
2. banana
3. mango
1. apple
2. banana
3. mango

zip — Combine Two Lists


    names = ["Rahul", "Priya", "Gagan"]
    marks = [85, 92, 78]

    for name, mark in zip(names, marks):
        print(f"{name}: {mark}")

Output:

Rahul: 85
Priya: 92
Gagan: 78

    # Create dictionary from two lists
    students_dict = dict(zip(names, marks))
    print(students_dict)    # {'Rahul': 85, 'Priya': 92, 'Gagan': 78}


any() and all()


    marks = [85, 92, 78, 45, 90]

    # all() — True if ALL items satisfy condition
    all_passed = all(m >= 50 for m in marks)
    print(all_passed)    # False — 45 fails

    # any() — True if AT LEAST ONE item satisfies condition
    anyone_failed = any(m < 50 for m in marks)
    print(anyone_failed)    # True — 45 fails

    # Practical example
    passwords = ["abc", "password123", "xyz"]
    has_strong = any(len(p) >= 8 for p in passwords)
    print(has_strong)    # True — password123 is long enough


Ternary Operator — One Line If/Else


    # Regular if/else
    age = 20
    if age >= 18:
        status = "Adult"
    else:
        status = "Minor"

    # Ternary — one line
    status = "Adult" if age >= 18 else "Minor"
    print(status)    # Adult

    # In f-strings
    age = 20
    print(f"You are {'Adult' if age >= 18 else 'Minor'}")


_ as Throwaway Variable

When you don't need a variable:


    # You need the index but not the value
    for _ in range(5):
        print("Hello")    # print 5 times, don't need loop variable

    # Unpacking but only want some values
    name, _, city = ("Gagan", 22, "Delhi")    # ignore age
    print(name, city)    # Gagan Delhi


Part 4 — Code Quality

Meaningful Names


    # Bad names
    x = 86400
    def calc(a, b):
        return a * b / 100

    # Good names
    SECONDS_IN_DAY = 86400
    def calculate_percentage(score, total):
        return score * total / 100


Constants in UPPER_CASE


    # Constants — values that never change
    MAX_RETRIES = 3
    DEFAULT_TIMEOUT = 30
    DATABASE_URL = "postgresql://localhost/mydb"
    PI = 3.14159


Functions Should Do One Thing


    # Bad — function doing too much
    def process_student(name, marks):
        average = sum(marks) / len(marks)
        if average >= 90: grade = "A"
        elif average >= 80: grade = "B"
        else: grade = "C"
        print(f"Name: {name}")
        print(f"Average: {average}")
        print(f"Grade: {grade}")
        with open("results.txt", "a") as f:
            f.write(f"{name},{average},{grade}\n")

    # Good — each function does one thing
    def calculate_average(marks):
        return sum(marks) / len(marks)

    def get_grade(average):
        if average >= 90: return "A"
        elif average >= 80: return "B"
        else: return "C"

    def print_result(name, average, grade):
        print(f"Name: {name}, Average: {average}, Grade: {grade}")

    def save_result(name, average, grade):
        with open("results.txt", "a") as f:
            f.write(f"{name},{average},{grade}\n")


Docstrings — Document Your Code


    def calculate_average(marks):
        """
        Calculate the average of a list of marks.

        Args:
            marks (list): List of numerical marks

        Returns:
            float: The average mark

        Example:
            >>> calculate_average([85, 90, 78])
            84.33
        """
        if not marks:
            return 0
        return sum(marks) / len(marks)

Docstrings appear when someone calls help(calculate_average). Professional code always has docstrings.


PEP 8 — Python Style Guide

PEP 8 is the official Python style guide. Key rules:


    # Indentation — 4 spaces (not tabs)
    def my_function():
        x = 10
        return x

    # Spaces around operators
    x = 5 + 3        # correct
    x=5+3            # wrong

    # No space before colon in dict/slice
    d = {"key": "value"}     # correct
    d = {"key" : "value"}    # wrong

    # Two blank lines between top-level functions/classes
    def function_one():
        pass


    def function_two():
        pass


    class MyClass:
        pass

    # One blank line between methods inside class
    class MyClass:
        def method_one(self):
            pass

        def method_two(self):
            pass

    # Max line length — 79 characters
    # Long lines — use backslash or parentheses
    result = (value_one + value_two +
            value_three + value_four)

    # Imports — one per line, at the top
    import os
    import json
    from datetime import datetime


Final Real World Example — Everything Together

A clean, Pythonic, well-structured program:


    """
    expense_tracker.py
    A simple expense tracker using Pythonic best practices.
    """

    import json
    import os
    from datetime import datetime

    EXPENSES_FILE = "expenses.json"
    CATEGORIES = ["food", "transport", "shopping", "bills", "entertainment", "other"]


    def load_expenses():
        """Load expenses from JSON file."""
        if not os.path.exists(EXPENSES_FILE):
            return []
        with open(EXPENSES_FILE, "r") as file:
            return json.load(file)


    def save_expenses(expenses):
        """Save expenses to JSON file."""
        with open(EXPENSES_FILE, "w") as file:
            json.dump(expenses, file, indent=4)


    def add_expense(expenses):
        """Add a new expense entry."""
        print("\nCategories:", ", ".join(CATEGORIES))
        description = input("Description: ").strip()
        category = input("Category: ").strip().lower()
       
        if category not in CATEGORIES:
            print(f"Invalid category. Using 'other'")
            category = "other"

        try:
            amount = float(input("Amount (Rs.): "))
            if amount <= 0:
                print("Amount must be positive")
                return
        except ValueError:
            print("Invalid amount")
            return

        expense = {
            "id": len(expenses) + 1,
            "description": description,
            "category": category,
            "amount": amount,
            "date": datetime.now().strftime("%Y-%m-%d")
        }

        expenses.append(expense)
        save_expenses(expenses)
        print(f"Expense added: Rs.{amount:.2f} for {description}")


    def view_expenses(expenses):
        """Display all expenses."""
        if not expenses:
            print("No expenses recorded yet")
            return

        print(f"\n{'ID':<5} {'Date':<12} {'Category':<15} {'Amount':<12} {'Description'}")
        print("-" * 60)

        for e in expenses:
            print(f"{e['id']:<5} {e['date']:<12} {e['category']:<15} Rs.{e['amount']:<10.2f} {e['description']}")

        total = sum(e["amount"] for e in expenses)
        print("-" * 60)
        print(f"{'Total:':<33} Rs.{total:.2f}")


    def view_by_category(expenses):
        """Show expense summary by category."""
        if not expenses:
            print("No expenses recorded yet")
            return

        category_totals = {}
        for e in expenses:
            cat = e["category"]
            category_totals[cat] = category_totals.get(cat, 0) + e["amount"]

        print("\n=== Expenses by Category ===")
        sorted_cats = sorted(category_totals.items(), key=lambda x: x[1], reverse=True)
        for category, total in sorted_cats:
            print(f"  {category:<15}: Rs.{total:.2f}")


    def filter_by_category(expenses):
        """Filter expenses by category."""
        category = input("Enter category: ").strip().lower()
        filtered = [e for e in expenses if e["category"] == category]

        if not filtered:
            print(f"No expenses in category '{category}'")
            return

        print(f"\n=== {category.title()} Expenses ===")
        for e in filtered:
            print(f"  {e['date']} — Rs.{e['amount']:.2f}{e['description']}")

        total = sum(e["amount"] for e in filtered)
        print(f"  Total: Rs.{total:.2f}")


    def main():
        """Main program loop."""
        expenses = load_expenses()
        print(f"Loaded {len(expenses)} existing expenses")

        menu_options = {
            "1": ("Add expense", add_expense),
            "2": ("View all expenses", view_expenses),
            "3": ("View by category", view_by_category),
            "4": ("Filter by category", filter_by_category),
            "5": ("Exit", None)
        }

        while True:
            print("\n=== Expense Tracker ===")
            for key, (label, _) in menu_options.items():
                print(f"{key}. {label}")

            choice = input("Choice: ").strip()

            if choice == "5":
                print("Goodbye!")
                break
            elif choice in menu_options:
                _, func = menu_options[choice]
                func(expenses)
            else:
                print("Invalid choice")


    if __name__ == "__main__":
        main()

Notice what's Pythonic here:

  • Docstrings on every function
  • List comprehension for filtering
  • sum() with generator expression
  • sorted() with lambda
  • Dictionary for menu options
  • .get() for safe dictionary access
  • if __name__ == "__main__" — standard entry point

if __name__ == "__main__" — Explained

def main():
    print("Program running")

if __name__ == "__main__":
    main()

When Python runs a file directly — __name__ equals "__main__" so main() runs. When another file imports this file — __name__ equals the filename so main() does NOT run automatically.

This is standard in every professional Python script. Always write it.


Exercise 🏋️ — Final Project

You've completed all 10 stages. Now build a complete CLI application combining everything:

Personal Finance Manager:

=== Personal Finance Manager ===
1. Add income
2. Add expense  
3. View transactions
4. View balance
5. Monthly summary
6. Export to file
7. Exit

Requirements:

  • OOP — Transaction class, Account class
  • File handling — save/load from JSON
  • Error handling — all inputs validated
  • Modules — datetime, json, os
  • Pythonic code — comprehensions, clean functions, docstrings
  • Categories for both income and expenses
  • Monthly summary showing income vs expense vs savings

This is your capstone project — it combines every single concept from all 10 stages. Take your time, build it properly.


🎉 Congratulations — You've Completed the Roadmap!

Let's look at everything you've learned:

Stage

Topics

Stage 1

Setup, print, variables, data types, math, input

Stage 2

If/else, elif, loops, break, continue

Stage 3

Functions, parameters, return, scope

Stage 4

Lists, tuples, dictionaries, sets

Stage 5

String methods, formatting, f-strings

Stage 6

File read/write, CSV, JSON files

Stage 7

Try/except, error types, raising exceptions

Stage 8

Built-in modules, pip, external libraries

Stage 9

Classes, objects, inheritance, polymorphism

Stage 10

Comprehensions, lambda, Pythonic patterns


What's Next After Core Python?

Now that you know Core Python — pick a direction:

Web Development:

  • Flask or Django framework
  • Build websites and APIs

Data Science / AI:

  • NumPy, Pandas, Matplotlib
  • Machine Learning with scikit-learn

Automation:

  • Selenium — automate browsers
  • PyAutoGUI — automate desktop
  • Schedule — run tasks automatically

Backend Development:

  • FastAPI — modern API framework
  • PostgreSQL with psycopg2
  • REST API development

Pythonic Code & Best Practices

Writing Code the Python Way What is "Pythonic" Code? You can solve any problem in many ways. But Python has a preferred style — ...