Python Basics — Coding Challenges

10 minute read

Coding Challenges

15 challenges covering everything from variables to file handling. Try to solve each one before checking the solution.


Easy (1–5)

Challenge 1: FizzBuzz

Print numbers 1 to 50. For multiples of 3, print “Fizz”. For multiples of 5, print “Buzz”. For multiples of both, print “FizzBuzz”.

Show Solution
for n in range(1, 51):
    if n % 15 == 0:
        print("FizzBuzz")
    elif n % 3 == 0:
        print("Fizz")
    elif n % 5 == 0:
        print("Buzz")
    else:
        print(n)

Challenge 2: Reverse a String

Write a function that reverses a string without using slicing ([::-1]) or reversed().

Show Solution
def reverse_string(text):
    result = ""
    for char in text:
        result = char + result
    return result

print(reverse_string("Python"))  # nohtyP
print(reverse_string("hello"))   # olleh

Challenge 3: Count Vowels

Write a function that counts the vowels in a string and returns a dictionary with each vowel’s count.

Show Solution
def count_vowels(text):
    vowel_counts = {v: 0 for v in "aeiou"}
    for char in text.lower():
        if char in vowel_counts:
            vowel_counts[char] += 1
    # Remove zeros
    return {v: c for v, c in vowel_counts.items() if c > 0}

print(count_vowels("Hello World"))
# {'e': 1, 'o': 2}

print(count_vowels("Programming is awesome"))
# {'o': 2, 'a': 2, 'i': 2, 'e': 2}

Challenge 4: Find Duplicates

Write a function that finds all duplicate values in a list.

Show Solution
def find_duplicates(items):
    seen = set()
    duplicates = set()
    for item in items:
        if item in seen:
            duplicates.add(item)
        seen.add(item)
    return sorted(duplicates)

print(find_duplicates([1, 2, 3, 2, 4, 5, 3, 6]))
# [2, 3]

print(find_duplicates(["a", "b", "a", "c", "b"]))
# ['a', 'b']

Challenge 5: Temperature Converter

Write functions to convert between Celsius, Fahrenheit, and Kelvin. Then build a simple CLI converter.

Show Solution
def celsius_to_fahrenheit(c):
    return (c * 9/5) + 32

def fahrenheit_to_celsius(f):
    return (f - 32) * 5/9

def celsius_to_kelvin(c):
    return c + 273.15

def kelvin_to_celsius(k):
    return k - 273.15

# Test
temps = [0, 100, 37, -40]
for c in temps:
    f = celsius_to_fahrenheit(c)
    k = celsius_to_kelvin(c)
    print(f"{c}°C = {f:.1f}°F = {k:.2f}K")

Output:

0°C = 32.0°F = 273.15K
100°C = 212.0°F = 373.15K
37°C = 98.6°F = 310.15K
-40°C = -40.0°F = 233.15K

Medium (6–10)

Challenge 6: Flatten Nested Lists

Write a function that flattens a nested list of any depth.

Input: [1, [2, 3], [4, [5, 6]], [7, [8, [9]]]] Output: [1, 2, 3, 4, 5, 6, 7, 8, 9]

Show Solution
def flatten(nested):
    result = []
    for item in nested:
        if isinstance(item, list):
            result.extend(flatten(item))  # Recursive call
        else:
            result.append(item)
    return result

print(flatten([1, [2, 3], [4, [5, 6]], [7, [8, [9]]]]))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

print(flatten([[1, 2], [3, [4, [5, [6]]]]]))
# [1, 2, 3, 4, 5, 6]

Challenge 7: Word Frequency Counter

Read a text string and return the top N most frequent words, ignoring case and punctuation.

Show Solution
import string

def top_words(text, n=5):
    # Clean and split
    cleaned = text.translate(str.maketrans("", "", string.punctuation)).lower()
    words = cleaned.split()
    
    # Count
    counts = {}
    for word in words:
        counts[word] = counts.get(word, 0) + 1
    
    # Sort by count (descending), then alphabetically
    sorted_words = sorted(counts.items(), key=lambda x: (-x[1], x[0]))
    return sorted_words[:n]

text = """
Python is great. Python is powerful. Python is easy to learn.
Programming with Python is fun. Python makes coding enjoyable.
"""

for word, count in top_words(text, 5):
    print(f"  {word:15} {count}")

Output:

  python               5
  is                   5
  programming          1
  with                 1
  great                1

Challenge 8: Matrix Operations

Implement matrix addition and multiplication using nested lists (no numpy).

Show Solution
def matrix_add(a, b):
    """Add two matrices."""
    rows = len(a)
    cols = len(a[0])
    return [
        [a[i][j] + b[i][j] for j in range(cols)]
        for i in range(rows)
    ]

def matrix_multiply(a, b):
    """Multiply two matrices."""
    rows_a, cols_a = len(a), len(a[0])
    rows_b, cols_b = len(b), len(b[0])
    
    assert cols_a == rows_b, "Incompatible dimensions"
    
    result = [[0] * cols_b for _ in range(rows_a)]
    for i in range(rows_a):
        for j in range(cols_b):
            for k in range(cols_a):
                result[i][j] += a[i][k] * b[k][j]
    return result

def print_matrix(m, label=""):
    if label:
        print(f"  {label}:")
    for row in m:
        print("  ", [f"{x:4}" for x in row])
    print()

# Test
a = [[1, 2], [3, 4]]
b = [[5, 6], [7, 8]]

print_matrix(matrix_add(a, b), "A + B")
# [[6, 8], [10, 12]]

print_matrix(matrix_multiply(a, b), "A × B")
# [[19, 22], [43, 50]]

Challenge 9: Caesar Cipher

Implement encryption and decryption for the Caesar cipher (shift each letter by N positions).

Show Solution
def caesar_encrypt(text, shift):
    """Encrypt text using Caesar cipher."""
    result = []
    for char in text:
        if char.isalpha():
            base = ord("A") if char.isupper() else ord("a")
            shifted = (ord(char) - base + shift) % 26 + base
            result.append(chr(shifted))
        else:
            result.append(char)
    return "".join(result)

def caesar_decrypt(text, shift):
    """Decrypt text using Caesar cipher."""
    return caesar_encrypt(text, -shift)

# Test
message = "Hello, World! Python is Fun!"
shift = 13

encrypted = caesar_encrypt(message, shift)
decrypted = caesar_decrypt(encrypted, shift)

print(f"Original:  {message}")
print(f"Encrypted: {encrypted}")
print(f"Decrypted: {decrypted}")

Output:

Original:  Hello, World! Python is Fun!
Encrypted: Uryyb, Jbeyq! Clguba vf Sha!
Decrypted: Hello, World! Python is Fun!

Challenge 10: Student Grade Manager

Read student data from a CSV string, calculate grades, and produce a report.

Show Solution
import csv
import io

csv_data = """name,math,science,english,history
Alice,92,88,95,90
Bob,78,82,70,75
Carol,95,91,89,93
Dave,65,72,80,68
Eve,88,85,92,87"""

def generate_report(csv_text):
    reader = csv.DictReader(io.StringIO(csv_text))
    students = []
    
    subjects = ["math", "science", "english", "history"]
    
    for row in reader:
        scores = {s: int(row[s]) for s in subjects}
        avg = sum(scores.values()) / len(scores)
        
        if avg >= 90: grade = "A"
        elif avg >= 80: grade = "B"
        elif avg >= 70: grade = "C"
        elif avg >= 60: grade = "D"
        else: grade = "F"
        
        students.append({
            "name": row["name"],
            "scores": scores,
            "average": avg,
            "grade": grade,
        })
    
    # Sort by average (descending)
    students.sort(key=lambda s: s["average"], reverse=True)
    
    # Print report
    print(f"{'Rank':<5} {'Name':<8} {'Math':>5} {'Sci':>5} {'Eng':>5} {'Hist':>5} {'Avg':>6} {'Grade':>6}")
    print("-" * 48)
    
    for rank, s in enumerate(students, 1):
        sc = s["scores"]
        print(f"{rank:<5} {s['name']:<8} {sc['math']:>5} {sc['science']:>5} "
              f"{sc['english']:>5} {sc['history']:>5} {s['average']:>6.1f} {s['grade']:>6}")
    
    # Subject averages
    print(f"\n{'Subject Averages':}")
    for subj in subjects:
        avg = sum(s["scores"][subj] for s in students) / len(students)
        print(f"  {subj.capitalize():<10} {avg:.1f}")

generate_report(csv_data)

Hard (11–15)

Challenge 11: Linked List Implementation

Implement a singly linked list with append, prepend, delete, search, and display methods.

Show Solution
class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

class LinkedList:
    def __init__(self):
        self.head = None
        self.size = 0
    
    def append(self, data):
        new_node = Node(data)
        if not self.head:
            self.head = new_node
        else:
            current = self.head
            while current.next:
                current = current.next
            current.next = new_node
        self.size += 1
    
    def prepend(self, data):
        new_node = Node(data)
        new_node.next = self.head
        self.head = new_node
        self.size += 1
    
    def delete(self, data):
        if not self.head:
            return False
        if self.head.data == data:
            self.head = self.head.next
            self.size -= 1
            return True
        current = self.head
        while current.next:
            if current.next.data == data:
                current.next = current.next.next
                self.size -= 1
                return True
            current = current.next
        return False
    
    def search(self, data):
        current = self.head
        index = 0
        while current:
            if current.data == data:
                return index
            current = current.next
            index += 1
        return -1
    
    def __repr__(self):
        items = []
        current = self.head
        while current:
            items.append(str(current.data))
            current = current.next
        return " → ".join(items) + " → None"
    
    def __len__(self):
        return self.size

# Test
ll = LinkedList()
for x in [10, 20, 30, 40]:
    ll.append(x)

ll.prepend(5)
print(ll)          # 5 → 10 → 20 → 30 → 40 → None
print(len(ll))     # 5
print(ll.search(30))  # 3
ll.delete(20)
print(ll)          # 5 → 10 → 30 → 40 → None

Challenge 12: Merge Sorted Lists

Write a function that merges two sorted lists into one sorted list without using sort().

Show Solution
def merge_sorted(list1, list2):
    """Merge two sorted lists into one sorted list."""
    result = []
    i = j = 0
    
    while i < len(list1) and j < len(list2):
        if list1[i] <= list2[j]:
            result.append(list1[i])
            i += 1
        else:
            result.append(list2[j])
            j += 1
    
    # Append remaining elements
    result.extend(list1[i:])
    result.extend(list2[j:])
    
    return result

# Test
a = [1, 3, 5, 7, 9]
b = [2, 4, 6, 8, 10]
print(merge_sorted(a, b))
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

c = [1, 1, 3]
d = [1, 2, 4, 5]
print(merge_sorted(c, d))
# [1, 1, 1, 2, 3, 4, 5]

Challenge 13: JSON Data Transformer

Write a function that reads a JSON file with nested data and transforms it into a flat CSV format.

Show Solution
import json
import csv

def flatten_json(obj, prefix=""):
    """Flatten a nested JSON object into key-value pairs."""
    items = {}
    for key, value in obj.items():
        new_key = f"{prefix}.{key}" if prefix else key
        if isinstance(value, dict):
            items.update(flatten_json(value, new_key))
        elif isinstance(value, list):
            items[new_key] = "; ".join(str(v) for v in value)
        else:
            items[new_key] = value
    return items

def json_to_flat_csv(json_data, csv_filename):
    """Convert a list of nested JSON objects to flat CSV."""
    # Flatten all records
    flat_records = [flatten_json(record) for record in json_data]
    
    # Collect all keys
    all_keys = set()
    for record in flat_records:
        all_keys.update(record.keys())
    fieldnames = sorted(all_keys)
    
    # Write CSV
    with open(csv_filename, "w", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames, extrasaction="ignore")
        writer.writeheader()
        writer.writerows(flat_records)
    
    print(f"Written {len(flat_records)} records to {csv_filename}")

# Test
data = [
    {
        "name": "Alice",
        "age": 30,
        "address": {"city": "NYC", "state": "NY"},
        "hobbies": ["reading", "coding"],
    },
    {
        "name": "Bob",
        "age": 25,
        "address": {"city": "LA", "state": "CA"},
        "hobbies": ["gaming", "hiking", "cooking"],
    },
]

json_to_flat_csv(data, "flat_output.csv")

# Verify
with open("flat_output.csv") as f:
    print(f.read())

Challenge 14: Decorator Collection

Implement three useful decorators: @timer, @retry, and @cache.

Show Solution
import time
import functools

# 1. Timer decorator
def timer(func):
    """Measure function execution time."""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        print(f"  {func.__name__}() took {elapsed:.4f}s")
        return result
    return wrapper

# 2. Retry decorator
def retry(max_attempts=3, delay=1):
    """Retry a function on failure."""
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            last_error = None
            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    last_error = e
                    if attempt < max_attempts:
                        time.sleep(delay)
            raise last_error
        return wrapper
    return decorator

# 3. Cache decorator
def cache(func):
    """Simple memoization cache."""
    memo = {}
    @functools.wraps(func)
    def wrapper(*args):
        if args not in memo:
            memo[args] = func(*args)
        return memo[args]
    wrapper.cache = memo
    return wrapper

# Test timer
@timer
def slow_sum(n):
    return sum(range(n))

slow_sum(1_000_000)
# slow_sum() took 0.0312s

# Test cache
@cache
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(f"  fib(30) = {fibonacci(30)}")      # 832040 (instant with cache)
print(f"  Cache size: {len(fibonacci.cache)}")  # 31

Challenge 15: Mini Database

Build a simple in-memory database with file persistence that supports basic operations: INSERT, SELECT (with WHERE), UPDATE, and DELETE.

Show Solution
import json
import os

class MiniDB:
    def __init__(self, filename="minidb.json"):
        self.filename = filename
        self.tables = self._load()
    
    def _load(self):
        if os.path.exists(self.filename):
            with open(self.filename) as f:
                return json.load(f)
        return {}
    
    def _save(self):
        with open(self.filename, "w") as f:
            json.dump(self.tables, f, indent=2)
    
    def create_table(self, name):
        if name not in self.tables:
            self.tables[name] = []
            self._save()
            print(f"  Table '{name}' created")
    
    def insert(self, table, record):
        if table not in self.tables:
            raise KeyError(f"Table '{table}' does not exist")
        self.tables[table].append(record)
        self._save()
    
    def select(self, table, where=None):
        if table not in self.tables:
            raise KeyError(f"Table '{table}' does not exist")
        rows = self.tables[table]
        if where:
            rows = [r for r in rows if all(r.get(k) == v for k, v in where.items())]
        return rows
    
    def update(self, table, where, updates):
        if table not in self.tables:
            raise KeyError(f"Table '{table}' does not exist")
        count = 0
        for row in self.tables[table]:
            if all(row.get(k) == v for k, v in where.items()):
                row.update(updates)
                count += 1
        self._save()
        return count
    
    def delete(self, table, where):
        if table not in self.tables:
            raise KeyError(f"Table '{table}' does not exist")
        before = len(self.tables[table])
        self.tables[table] = [
            r for r in self.tables[table]
            if not all(r.get(k) == v for k, v in where.items())
        ]
        self._save()
        return before - len(self.tables[table])

# Test
db = MiniDB("test_db.json")
db.create_table("users")

db.insert("users", {"name": "Alice", "age": 30, "role": "admin"})
db.insert("users", {"name": "Bob", "age": 25, "role": "user"})
db.insert("users", {"name": "Carol", "age": 35, "role": "user"})

print("\nAll users:")
for u in db.select("users"):
    print(f"  {u}")

print("\nAdmins:")
for u in db.select("users", where={"role": "admin"}):
    print(f"  {u}")

updated = db.update("users", {"name": "Bob"}, {"age": 26})
print(f"\nUpdated {updated} record(s)")

deleted = db.delete("users", {"name": "Carol"})
print(f"Deleted {deleted} record(s)")

print("\nFinal state:")
for u in db.select("users"):
    print(f"  {u}")

# Cleanup
os.remove("test_db.json")

Self-Scoring Guide

Score Level Recommendation
13-15 Expert You’re ready for Python OOP and advanced topics
9-12 Advanced Strong foundation — review tricky areas
5-8 Intermediate Good progress — practice more with medium challenges
1-4 Beginner Review earlier chapters and try easy challenges again

Next up: Common Mistakes →

Updated: