Python 3.12+ · Updated November 2026 · 40 exercises with solutions

Python Practice Exercises
40 hands-on problems with full solutions

A curated set of beginner-friendly exercises that build fluency with Python's core language features. Every exercise has a clear task description, starter code, expected output, and a fully explained solution — try it yourself before expanding the answer.

Exercise 1 — Greet the user

name = "Ana"
print(f"Hello, {name}!")
Written & reviewed by Glenn Junsay Pansensoy Last reviewed: November 2026 Python 3.12+ ~60 min total

📚 What This Exercise Set Covers

40 exercises grouped by topic — do them in order or jump to any section

🧭 How to Use These Exercises

A repeatable process that turns solving into learning

The recommended workflow

1. Read the task. Understand what the exercise asks before looking at any code.
2. Try it yourself. Give yourself 15 minutes of genuine effort — write your own version in the starter code, run it, see what happens.
3. Only then check the solution. Compare your approach to the provided one. Both may be valid — the point is what you learned in the gap.
4. Re-implement the solution from memory. Close the solution panel, delete your code, and write the whole thing again. This step is where the learning really happens.

💡 If you get stuck on the same exercise for more than 20 minutes, read the solution — that's not cheating, it's efficient. The learning still happens when you re-implement it from memory afterwards. What doesn't work is staring at the same error for an hour without progress.

🟢 Basics (Exercises 1–10)

Variables, arithmetic, conditionals, and simple loops

1

Greet the User

Print a greeting that includes the user's name.

● Easy

Task: Create a variable name with any value. Print a greeting in the form Hello, [name]!.

# Your code here
name = "Ana"
print(f"Hello, {name}!")
Expected outputHello, Ana!
Show solution
Solution explained
  1. 1Line 1 is a comment — a note for human readers, ignored by Python.
  2. 2name = "Ana" creates a variable holding the string "Ana".
  3. 3The f-string f"Hello, {name}!" substitutes the value of name into the string, then prints it.
2

Sum of Two Numbers

Add two integers and print the result.

● Easy

Task: Create two variables a and b, each holding an integer. Compute their sum and print it.

a = 7
b = 3
print(a + b)
Expected output10
Show solution
Solution explained
  1. 1a = 7 stores the integer 7.
  2. 2b = 3 stores the integer 3.
  3. 3print(a + b) computes 7 + 3 = 10 and prints it.
3

Even or Odd

Determine whether a number is even or odd.

● Easy

Task: Given a variable n, print "even" if it's divisible by 2, otherwise print "odd".

n = 17
if n % 2 == 0:
    print("even")
else:
    print("odd")
Expected outputodd
Show solution
Solution explained
  1. 1n = 17 — the value to test.
  2. 2n % 2 == 0 — the modulo operator % returns the remainder. If it's 0, the number is even.
  3. 3Branch taken when n is even.
  4. 4The else branch runs when the condition is false.
  5. 5Prints "odd" — 17 divided by 2 leaves a remainder of 1.
4

Count to Ten

Print the numbers from 1 to 10, one per line.

● Easy

Task: Use a loop to print each number from 1 to 10 on its own line.

for i in range(1, 11):
    print(i)
Expected output1 2 3 4 5 6 7 8 9 10
Show solution
Solution explained
  1. 1range(1, 11) yields 1 through 10. Remember: the stop value (11) is excluded.
  2. 2Each iteration prints the current value of i.
5

Sum from 1 to N

Compute the sum of the first N positive integers.

● Easy

Task: Given n = 100, compute and print the sum 1 + 2 + … + 100.

n = 100
total = 0
for i in range(1, n + 1):
    total += i
print(total)
Expected output5050
Show solution
Solution explained
  1. 1n = 100 — the upper limit.
  2. 2total is the accumulator, starting at 0.
  3. 3range(1, n + 1) yields 1 through 100 inclusive.
  4. 4total += i adds each number to the running total.
  5. 5Prints the final sum: 5050.

Shorter version: print(sum(range(1, 101))) does the same thing in one line — but the loop version makes the mechanism visible, which is the point of the exercise.

6

Largest of Three

Find the maximum among three numbers.

● Easy

Task: Given three variables a, b, c, print the largest one.

a = 12
b = 45
c = 23

print(max(a, b, c))
Expected output45
Show solution
Solution explained
  1. 1–3Three variables with different values.
  2. 5max() accepts any number of arguments and returns the largest.

Manual alternative: if a >= b and a >= c: print(a), and so on — instructive to write once, but max() is what you'd use in real code.

7

FizzBuzz (Mini)

The classic coding exercise, simplified to a single number.

● Medium

Task: Given n, print "Fizz" if divisible by 3, "Buzz" if divisible by 5, "FizzBuzz" if both, otherwise the number itself.

n = 15

if n % 15 == 0:
    print("FizzBuzz")
elif n % 3 == 0:
    print("Fizz")
elif n % 5 == 0:
    print("Buzz")
else:
    print(n)
Expected outputFizzBuzz
Show solution
Solution explained
  1. 1n = 15 — the value to classify.
  2. 3Check the "both" case first — otherwise Fizz or Buzz would win and we'd never reach FizzBuzz. The order of the if/elif chain matters.
  3. 4–7Individual cases for divisible by 3 or by 5.
  4. 8–9The else branch prints the number itself.
8

Multiplication Table

Print the 7× table.

● Easy

Task: Print the multiplication table for 7, from 7×1 through 7×10.

for i in range(1, 11):
    print(f"7 × {i} = {7 * i}")
Expected output7 × 1 = 7 7 × 2 = 14 7 × 3 = 21 7 × 4 = 28 7 × 5 = 35 7 × 6 = 42 7 × 7 = 49 7 × 8 = 56 7 × 9 = 63 7 × 10 = 70
Show solution
Solution explained
  1. 1Loop from 1 to 10.
  2. 2F-string computes 7 * i and formats the whole line.
9

Countdown

Print numbers from 10 down to 1, then "Liftoff!".

● Easy

Task: Use a loop to count down from 10, then print "Liftoff!".

for i in range(10, 0, -1):
    print(i)
print("Liftoff!")
Expected output10 9 8 7 6 5 4 3 2 1 Liftoff!
Show solution
Solution explained
  1. 1range(10, 0, -1) — start at 10, stop before 0, step −1. Yields 10, 9, …, 1.
  2. 2Prints each countdown number.
  3. 3Runs once after the loop finishes — not inside it.
10

Factorial

Compute 5! (the factorial of 5).

● Medium

Task: Compute and print 5!, which is 5 × 4 × 3 × 2 × 1.

n = 5
result = 1
for i in range(1, n + 1):
    result *= i
print(result)
Expected output120
Show solution
Solution explained
  1. 1n = 5 — the number whose factorial we want.
  2. 2result = 1 — the identity for multiplication. Must start at 1, not 0.
  3. 3Loop 1 through 5.
  4. 4result *= i multiplies the running product by each value.
  5. 5Prints 120 — 5 × 4 × 3 × 2 × 1.

📝 Strings (Exercises 11–18)

Indexing, slicing, methods, and f-strings

11

Reverse a String

Print a string backwards.

● Easy

Task: Given s = "Python", print the string in reverse.

s = "Python"
print(s[::-1])
Expected outputnohtyP
Show solution
Solution explained
  1. 1The string to reverse.
  2. 2s[::-1] is a slice with step −1. It walks the string from end to start, producing a reversed copy. The original s is unchanged (strings are immutable).
12

Count Vowels

Count how many vowels appear in a word.

● Medium

Task: Given a word, count how many of its characters are vowels (aeiou, case-insensitive).

word = "Programming"
vowels = "aeiou"

count = 0
for ch in word.lower():
    if ch in vowels:
        count += 1
print(count)
Expected output3
Show solution
Solution explained
  1. 1The word to inspect.
  2. 2The set of vowel characters — lowercase only, so we lower() the input.
  3. 4Accumulator starting at 0.
  4. 5word.lower() makes the check case-insensitive — "Programming" contains "P" and "o", which lowercase to "p" and "o".
  5. 6–7Increments count when the character is a vowel. "Programming" → o, a, i = 3.
  6. 8Prints 3.
13

Palindrome Check

Check if a word reads the same forwards and backwards.

● Medium

Task: Given a word, print True if it's a palindrome (reads the same forwards and backwards), otherwise False.

word = "racecar"
cleaned = word.lower()
print(cleaned == cleaned[::-1])
Expected outputTrue
Show solution
Solution explained
  1. 1The word to test.
  2. 2Lowercase copy — makes the comparison case-insensitive.
  3. 3Compares the cleaned string to its reversed version. If they're equal, it's a palindrome. "racecar" reversed is "racecar" — same.
14

Title Case a Sentence

Capitalize the first letter of every word.

● Easy

Task: Given a sentence, capitalize each word's first letter and lowercase the rest.

sentence = "the quick brown fox"
print(sentence.title())
Expected outputThe Quick Brown Fox
Show solution
Solution explained
  1. 1The input string.
  2. 2.title() uppercases the first character of each word and lowercases the rest. Simple and idiomatic.
15

Word Lengths

Print the length of every word in a sentence.

● Medium

Task: For each word in a sentence, print the word and its length.

sentence = "Python is fun to learn"

for word in sentence.split():
    print(f"{word}: {len(word)}")
Expected outputPython: 6 is: 2 fun: 3 to: 2 learn: 5
Show solution
Solution explained
  1. 1The sentence to analyse.
  2. 3.split() with no argument splits on whitespace — ['Python', 'is', 'fun', 'to', 'learn'].
  3. 4Print each word with its length.
16

Replace and Clean

Normalize a string of phone numbers.

● Medium

Task: Given " (555) 123-4567 ", remove spaces, parentheses, and dashes, leaving just the digits.

phone = "  (555) 123-4567  "
cleaned = ""
for ch in phone:
    if ch.isdigit():
        cleaned += ch
print(cleaned)
Expected output5551234567
Show solution
Solution explained
  1. 1The raw phone string with spaces and punctuation.
  2. 2Accumulator for the cleaned digits.
  3. 3Iterates over each character.
  4. 4.isdigit() returns True only for 0–9.
  5. 5Appends digits, skips everything else.
  6. 6Result: "5551234567".

Shorter: print("".join(c for c in phone if c.isdigit())) — uses a generator expression instead of an explicit accumulator.

17

First Non-Repeating Character

Find the first character that appears only once.

● Hard

Task: Given "swiss", find the first character that appears exactly once.

word = "swiss"
counts = {}
for ch in word:
    counts[ch] = counts.get(ch, 0) + 1

for ch in word:
    if counts[ch] == 1:
        print(ch)
        break
Expected outputw
Show solution
Solution explained
  1. 1The input word.
  2. 2Dictionary to store character counts.
  3. 3–4First pass: build the count map. .get(ch, 0) + 1 handles missing keys without a separate check.
  4. 6–9Second pass: iterate in original order. The first character with count 1 is the answer. break stops the loop early.

Result: "w" — "s" appears 3 times, "i" appears once, but "w" appears first among the unique characters.

18

String Compression (Basic)

Compress runs of repeated characters.

● Hard

Task: Given "aaabbbcc", print "a3b3c2" — each character followed by the count of consecutive repeats.

s = "aaabbbcc"

result = []
prev = s[0]
count = 1

for ch in s[1:]:
    if ch == prev:
        count += 1
    else:
        result.append(f"{prev}{count}")
        prev = ch
        count = 1

result.append(f"{prev}{count}")
print("".join(result))
Expected outputa3b3c2
Show solution
Solution explained
  1. 1The input string.
  2. 3Accumulator for the output chunks.
  3. 4–5prev tracks the current run's character; count counts its length. Start with the first character.
  4. 7Iterate from the second character onward.
  5. 8–9If the character matches prev, increment the count.
  6. 10–12Otherwise, the run has ended — emit prev + count, then start a new run.
  7. 14After the loop, emit the final run — it has no following character to trigger the else-branch.
  8. 15Join the chunks into one string.

📦 Collections (Exercises 19–28)

Lists, dicts, sets, and comprehensions

19

Sum a List

Add up all numbers in a list.

● Easy

Task: Given a list of numbers, print their sum.

numbers = [4, 7, 2, 9, 1]
print(sum(numbers))
Expected output23
Show solution
Solution explained
  1. 1The list of numbers.
  2. 2sum() is a built-in that adds all elements. Fast and idiomatic.
20

Filter Even Numbers

Build a new list containing only the even numbers.

● Easy

Task: Given a list of integers, produce a new list with only the even values.

nums = [1, 2, 3, 4, 5, 6]
evens = [n for n in nums if n % 2 == 0]
print(evens)
Expected output[2, 4, 6]
Show solution
Solution explained
  1. 1The input list.
  2. 2List comprehension with an if filter after the for.
  3. 3Prints [2, 4, 6].
21

Find the Maximum Without max()

Manually find the largest element.

● Medium

Task: Given a non-empty list, find the largest value using a loop (don't use max()).

nums = [3, 8, 1, 9, 4]

largest = nums[0]
for n in nums[1:]:
    if n > largest:
        largest = n
print(largest)
Expected output9
Show solution
Solution explained
  1. 1The list to inspect.
  2. 3Initialise with the first element — must be an actual list value, not 0.
  3. 4–6Iterate over the rest. Whenever a bigger value is found, update largest.
  4. 7Prints 9.
22

Count Word Frequency

Count how many times each word appears.

● Medium

Task: Given a list of words, build a dict mapping each word to its count.

words = ["apple", "banana", "apple", "cherry", "banana", "apple"]

counts = {}
for w in words:
    counts[w] = counts.get(w, 0) + 1

print(counts)
Expected output{'apple': 3, 'banana': 2, 'cherry': 1}
Show solution
Solution explained
  1. 1List of words with duplicates.
  2. 3Empty dict for counts.
  3. 4–5.get(w, 0) + 1 — returns the current count (or 0) plus one. Assigns back to the same key.
  4. 7Prints the frequency map.

Shortcut: from collections import Counter; Counter(words) does the same in one line — and the result is a dict subclass.

23

Merge Two Lists

Combine two lists into one, alternating items.

● Medium

Task: Given two equal-length lists, interleave their items.

a = [1, 3, 5]
b = [2, 4, 6]

merged = [item for pair in zip(a, b) for item in pair]
print(merged)
Expected output[1, 2, 3, 4, 5, 6]
Show solution
Solution explained
  1. 1–2Two input lists.
  2. 4zip(a, b) produces pairs (1, 2), (3, 4), (5, 6). The outer comprehension iterates over pairs; the inner iterates over the two items within each pair.
  3. 5Prints the interleaved list.
24

Deduplicate Preserving Order

Remove duplicates but keep the original order.

● Medium

Task: Given a list with repeated values, produce a new list where each value appears once — in the order it first appeared.

nums = [3, 1, 2, 3, 1, 4, 2]
unique = list(dict.fromkeys(nums))
print(unique)
Expected output[3, 1, 2, 4]
Show solution
Solution explained
  1. 1The input list with duplicates.
  2. 2dict.fromkeys(nums) creates a dict with each element as a key. Since Python 3.7, dicts preserve insertion order — duplicates are dropped but the first occurrence order is kept. list(...) converts the dict's keys back to a list.
  3. 3Prints [3, 1, 2, 4].

A plain set(nums) would also deduplicate, but the order would be lost. This is why the dict.fromkeys trick is the preferred idiom when order matters.

25

List of Squares (Comprehension)

Build a list of squares using a comprehension.

● Easy

Task: Using a single expression, build a list [1, 4, 9, 16, 25].

squares = [n * n for n in range(1, 6)]
print(squares)
Expected output[1, 4, 9, 16, 25]
Show solution
Solution explained
  1. 1range(1, 6) yields 1–5. Each is squared in the expression.
  2. 2Prints the list.
26

Total Per Category

Sum amounts grouped by category.

● Hard

Task: Given a list of (category, amount) tuples, build a dict of total amount per category.

transactions = [
    ("food", 12.50),
    ("transport", 3.00),
    ("food", 8.00),
    ("entertainment", 15.00),
    ("food", 5.00),
]

totals = {}
for category, amount in transactions:
    totals[category] = totals.get(category, 0) + amount

print(totals)
Expected output{'food': 25.5, 'transport': 3.0, 'entertainment': 15.0}
Show solution
Solution explained
  1. 1–7The transaction list — each entry is a (category, amount) tuple.
  2. 9Empty dict for accumulating totals.
  3. 10–11Unpack each tuple into category and amount. Add amount to the running total for that category. .get(category, 0) initialises new categories at 0.
  4. 13Prints the totals dict.
27

Find Common Elements

Find the intersection of two lists.

● Medium

Task: Given two lists, print the elements they have in common.

a = [1, 2, 3, 4]
b = [3, 4, 5, 6]

common = list(set(a) & set(b))
print(sorted(common))
Expected output[3, 4]
Show solution
Solution explained
  1. 1–2Two input lists.
  2. 4set(a) & set(b) — the & operator on sets produces the intersection. list(...) converts back to a list.
  3. 5sorted() ensures predictable output order (sets are unordered).
28

Transpose a Grid

Swap rows and columns of a 2D list.

● Hard

Task: Given a 3×3 grid, produce its transpose (rows become columns).

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

transposed = [list(row) for row in zip(*grid)]
for row in transposed:
    print(row)
Expected output[1, 4, 7] [2, 5, 8] [3, 6, 9]
Show solution
Solution explained
  1. 1–5The input grid — a list of three rows.
  2. 7zip(*grid) unpacks the three rows into arguments to zip, producing three column-tuples. list(row) converts each tuple to a list for consistency with the input type.
  3. 8–9Prints each transposed row.

🔧 Functions (Exercises 29–35)

Defining reusable functions with parameters and return values

29

Basic Function

Write a function that returns a greeting.

● Easy

Task: Define a function greet(name) that returns "Hello, [name]!".

def greet(name):
    return f"Hello, {name}!"

print(greet("Ana"))
print(greet("Bob"))
Expected outputHello, Ana! Hello, Bob!
Show solution
Solution explained
  1. 1def greet(name): defines a function named greet that accepts one parameter.
  2. 2return sends back the greeting string — the function's output.
  3. 4–5Two calls with different arguments prove the parameter works.
30

Function with Default Parameter

Add an optional argument with a sensible default.

● Easy

Task: Define greet(name, greeting="Hello") where greeting can be overridden.

def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

print(greet("Ana"))
print(greet("Bob", greeting="Hi"))
Expected outputHello, Ana! Hi, Bob!
Show solution
Solution explained
  1. 1greeting="Hello" is a default parameter value — used when the caller doesn't supply the argument.
  2. 2The f-string interpolates both.
  3. 4Called without greeting, the default "Hello" is used.
  4. 5Called with an explicit greeting="Hi", the default is overridden.
31

Function Returning Multiple Values

Return several values at once.

● Medium

Task: Define min_max(nums) that returns both the smallest and largest values in a list.

def min_max(nums):
    return min(nums), max(nums)

nums = [4, 1, 8, 2, 9]
small, large = min_max(nums)
print(f"min={small} max={large}")
Expected outputmin=1 max=9
Show solution
Solution explained
  1. 1–2return min(nums), max(nums) — the comma creates a tuple. Python returns it as a single tuple object.
  2. 4Input list.
  3. 5Tuple unpacking: the returned tuple is split into two variables.
  4. 6Prints both values.
32

Variable Number of Arguments

Accept any number of numbers and sum them.

● Medium

Task: Define sum_all(*args) that adds any number of arguments.

def sum_all(*args):
    return sum(args)

print(sum_all(1, 2, 3))
print(sum_all(10))
print(sum_all())
Expected output6 10 0
Show solution
Solution explained
  1. 1*args collects all positional arguments into a tuple.
  2. 2sum(args) adds them. For an empty tuple, sum() returns 0.
  3. 4–6Three calls with different numbers of arguments — the same function handles all cases.
33

Lambda Function

Sort a list of dictionaries by a key.

● Medium

Task: Given a list of student dicts, sort them by score (descending).

students = [
    {"name": "Ana", "score": 95},
    {"name": "Bob", "score": 88},
    {"name": "Cara", "score": 92},
]

sorted_students = sorted(students, key=lambda s: s["score"], reverse=True)
for s in sorted_students:
    print(f"{s['name']}: {s['score']}")
Expected outputAna: 95 Cara: 92 Bob: 88
Show solution
Solution explained
  1. 1–5A list of student dicts.
  2. 7key=lambda s: s["score"] — the lambda tells sorted which field to sort by. Without it, sorted would try to compare dicts, which raises TypeError.
  3. 8reverse=True sorts highest first.
  4. 9–10Prints the sorted list.
34

Recursive Factorial

Rewrite Exercise 10 using recursion.

● Hard

Task: Define factorial(n) recursively. factorial(0) is 1; factorial(n) is n × factorial(n-1).

def factorial(n):
    if n <= 1:
        return 1
    return n * factorial(n - 1)

print(factorial(5))
print(factorial(0))
Expected output120 1
Show solution
Solution explained
  1. 1Function definition.
  2. 2–3Base case — for n ≤ 1, factorial is 1. Without this, the recursion would never terminate.
  3. 4Recursive case — calls itself with n - 1.
  4. 6factorial(5) → 5 × factorial(4) → … → 5 × 4 × 3 × 2 × 1 = 120.
  5. 7Base case returns 1 for n=0.

Recursion is elegant for small inputs but can hit Python's recursion limit (~1000 by default) for large n. For anything beyond toy sizes, the iterative version from Exercise 10 or math.factorial is the right choice.

35

Higher-Order Function

Write a function that takes another function as an argument.

● Hard

Task: Define apply_twice(func, x) that applies func to x twice.

def apply_twice(func, x):
    return func(func(x))

print(apply_twice(lambda n: n + 10, 5))
print(apply_twice(lambda n: n * 2, 3))
Expected output25 12
Show solution
Solution explained
  1. 1apply_twice takes a function and a value.
  2. 2func(func(x)) applies func to x, then applies it again to the result.
  3. 4With +10: 5 + 10 = 15, then 15 + 10 = 25.
  4. 5With *2: 3 * 2 = 6, then 6 * 2 = 12.

🏛️ Classes (Exercises 36–40)

Object-oriented Python — defining classes, methods, and properties

36

A Simple Class

Define a class with attributes and a method.

● Medium

Task: Define a Dog class with name and breed attributes, and a bark() method that returns "Woof!".

class Dog:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed

    def bark(self):
        return "Woof!"

d = Dog("Rex", "Labrador")
print(f"{d.name} says {d.bark()}")
Expected outputRex says Woof!
Show solution
Solution explained
  1. 1class Dog: starts the class definition.
  2. 2__init__ is the constructor — runs when a new instance is created.
  3. 3–4Store the arguments as instance attributes.
  4. 6–7bark takes self and returns a fixed string.
  5. 9Creates an instance: d is a Dog.
  6. 10Access attributes and call a method. Result: "Rex says Woof!".
37

Class with @property

Expose a computed value as an attribute.

● Medium

Task: Define a Rectangle class with width and height, and an area property that returns width × height.

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    @property
    def area(self):
        return self.width * self.height

r = Rectangle(4, 5)
print(f"Area: {r.area}")
Expected outputArea: 20
Show solution
Solution explained
  1. 1Class definition.
  2. 2–4Constructor stores width and height.
  3. 6@property turns the following method into an attribute-like accessor.
  4. 7–8area computes and returns the product — but callers use it as r.area, not r.area().
  5. 10–11Creates a rectangle and prints its area using property access.
38

Inheritance

Extend a base class with a subclass.

● Hard

Task: Define Animal with speak(), then a Dog subclass that overrides speak().

class Animal:
    def speak(self):
        return "..."

class Dog(Animal):
    def speak(self):
        return "Woof!"

for animal in [Animal(), Dog()]:
    print(animal.speak())
Expected output... Woof!
Show solution
Solution explained
  1. 1–3Base class with a generic speak.
  2. 5class Dog(Animal): — Dog inherits from Animal.
  3. 6–7speak overrides the base implementation.
  4. 9–10Both objects respond to .speak() — this is polymorphism. The base class returns "..."; the override returns "Woof!".
39

Custom Exception

Define a custom exception class and raise it.

● Medium

Task: Define ValidationError (a subclass of Exception) and a function validate_age(age) that raises it for negative values.

class ValidationError(Exception):
    pass

def validate_age(age):
    if age < 0:
        raise ValidationError("Age cannot be negative")
    return age

try:
    validate_age(-5)
except ValidationError as e:
    print(f"Caught: {e}")
Expected outputCaught: Age cannot be negative
Show solution
Solution explained
  1. 1–2ValidationError inherits from Exception. The body is pass because the class name itself is the message — it's meaningful to callers reading the code.
  2. 4–7Raises the exception for negative ages, otherwise returns the value.
  3. 9–12try/except catches the custom exception and prints its message.
40

Dataclass

Define a data-holding class with minimal boilerplate.

● Medium

Task: Use @dataclass to define a Point with x and y. Verify that two identical points compare equal.

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

p1 = Point(3, 4)
p2 = Point(3, 4)

print(p1)
print(p1 == p2)
Expected outputPoint(x=3, y=4) True
Show solution
Solution explained
  1. 1Import the decorator from the standard library.
  2. 3@dataclass auto-generates __init__, __repr__, and __eq__ from the annotated fields.
  3. 4–6Class with two fields and no explicit methods — that's the point.
  4. 8–9Two separate instances with identical data.
  5. 11Auto-generated __repr__ gives a readable form: Point(x=3, y=4).
  6. 12Auto-generated __eq__ makes == compare fields. Prints True.
🎉 That's all 40 exercises. If you've worked through them all and can re-implement the solutions from memory, you've built genuine fluency with Python's core features. The next step is projects — pick one of the ideas from the projects section and build it end-to-end.

❓ Frequently Asked Questions

Common questions about using these exercises

Two to three per day is a good rhythm for beginners. The point of practice is understanding, not speed — it is far better to fully grasp three exercises than to skim through ten without absorbing them. Aim for 15–20 minutes on each exercise, including time spent reading the solution if you get stuck.

Give yourself 15 minutes of genuine effort — try different approaches, print intermediate values, and read any error message slowly. If you're still stuck after that, read the solution and then re-implement it from memory without looking. Re-implementing is where the learning happens; simply reading code is not the same as writing it.

No. These exercises build fluency with Python's core language features, but real jobs require project work, testing, version control, reading other people's code, and domain knowledge. Treat these exercises as your foundation, then move on to building projects: a CLI tool, a web scraper, a small API, a data visualisation. The exercises prepare you for those projects.

Yes — the exercises are free to use and share. Attribution back to Poetic Bytes is appreciated but not required for educational use. If you're teaching, the numbering makes it easy to assign specific problems (e.g. "do exercises 1–10 tonight").

Educational Use Disclaimer

This page is part of Poetic Bytes, a free educational resource for learning Python programming. The code examples are provided for instruction and demonstration. They are not production-audited and should not be used as-is in security-sensitive, financial, or safety-critical systems without independent review. See our full disclaimer and terms of use.

🗣️ Learner Voices

No comments yet — your voice starts the conversation.