Programming and IT
Python: practice problems with solutions
A set of problems in order of difficulty — from reversing a string to a generator and a call-counting decorator. Solve each one yourself first and only then open the walkthrough: comparing your code with someone else's teaches more than reading a finished answer. All solutions are tested on Python 3.
In this article
How to solve them#
The routine is always the same: read the statement, write down two or three "input → expected output" examples, write a solution, run it on the examples, and only then look at the walkthrough. If you cannot get a solution down in ten minutes, go back to the theory linked at the start of the section and try again: peeking at the answer before that point is of little use.
A useful habit: after every solution, ask yourself "what breaks if the input is empty?" Half the problems below have such an edge case, and the walkthrough points it out.
Strings#
Theory: Python strings.
Problem 1. Palindrome. Check whether a phrase reads the same both ways,
ignoring spaces, punctuation and case. "A man, a plan, a canal: Panama" →
True, "hello" → False.
=
return ==
Walkthrough: the generator expression inside join keeps only letters and
lowercases them, and [::-1] reverses the string. Compare the cleaned string
with its reverse, not the original. By this code an empty string is a palindrome,
which is reasonable.
Problem 2. Vowels. Count the vowels in an English word. "Programming" → 3.
return
Walkthrough: sum over a generator of ones is the idiom for "count how many
items match". Decide up front what to do with y: in "rhythm" it acts as a
vowel, and this code counts zero vowels there. Whatever you choose, write it
down as part of the problem.
Problem 3. Initials. Turn the string "john william smith" into
"J. W. Smith".
=
return f
Walkthrough: split() with no argument splits on any whitespace and leaves no
empty items, even if there are several spaces between words. Edge case: with
fewer than three parts the code fails with IndexError — a production version
should check len(parts) == 3.
Lists and slices#
Theory: Python lists and Python slicing.
Problem 4. Second largest. Find the second-largest distinct value in a list.
[5, 1, 5, 3, 9, 9] → 5.
=
return
Walkthrough: set removes duplicates, so the two nines count as one value and
the answer is 5, not 9. The length check covers the case where all items are
equal.
Problem 5. Chunks of n. Split a list into sublists of n items; the last
chunk may be shorter. ([1..7], 3) → [[1, 2, 3], [4, 5, 6], [7]].
return
Walkthrough: range with a step gives the starting indexes, and a slice stops at
the end of the list on its own — so the last chunk comes out shorter without any
conditions. That is the main advantage of a slice: it does not raise an exception
past the boundary.
Problem 6. Common items in order. Return the values found in both lists, in
the order of the first list and without duplicates.
([3, 1, 2, 3, 4], [4, 3, 5]) → [3, 4].
=
=
return
Walkthrough: a set made from the second list gives fast membership checks — on a
list they are linear, on a set constant. A plain set(a) & set(b) also gives the
right values but loses the order.
Dictionaries#
Theory: Python dictionaries.
Problem 7. The three most common words. Count word frequencies in a string and print the three most common; on a tie, sort alphabetically.
=
= + 1
=
Walkthrough: get(word, 0) saves you the "is the key there" check. The sort key
is a tuple: the minus in front of the count gives descending order by number, and
the second field sorts equally frequent words alphabetically. For the string
"cat and dog and cat and mouse cat dog" the result is
[('and', 3), ('cat', 3), ('dog', 2)].
Problem 8. Group by first letter. Build a "letter → list of words" dictionary from a list of words.
=
Walkthrough: setdefault returns the existing list or stores a new empty one and
returns it — one line instead of three with a check. Note that "A" and "a" are
different keys; lowercase the words first if that matters.
Problem 9. Totals by category. From a list of pairs like ("tea", 120), build
a dictionary with the total for each name.
=
= +
Walkthrough: unpacking the pair right in the loop header reads better than
indexing with pair[0] and pair[1]. For
[("tea", 120), ("coffee", 250), ("tea", 80)] you get
{'tea': 200, 'coffee': 250}.
Functions, generators, decorators#
Theory: Python functions, Python generators and Python decorators.
Problem 10. Mean without the extremes. A function takes any number of values
and returns the mean without one minimum and one maximum.
(1, 5, 6, 7, 100) → 6.0.
=
return /
Walkthrough: *values collects the arguments into a tuple, and
sorted(...)[1:-1] drops the extremes. The length check is required: with two
values the slice would be empty and you would divide by zero.
Problem 11. Fibonacci up to a limit. A generator that yields Fibonacci numbers
not exceeding a given value. 50 → 0 1 1 2 3 5 8 13 21 34.
, = 0, 1
yield
, = , +
Walkthrough: the simultaneous assignment a, b = b, a + b evaluates the whole
right-hand side before writing, so no temporary variable is needed. The generator
does not build a list — values arrive one at a time, and the limit can be
anything.
Problem 12. Call counter. A decorator that counts how many times a function
has been called and stores the number in a calls attribute.
+= 1
return
= 0
return
Walkthrough: the counter lives as an attribute of the wrapper itself, so it is
visible from outside as ping.calls. The line wrapper.calls = 0 comes after the
wrapper is defined but before it is returned: by the time of the first call, the
attribute already exists.
Files and exceptions#
Theory: reading files in Python and Python exceptions.
Problem 13. Sum of numbers in a file. Read a file line by line, add up the numbers, skip non-numeric lines and count them separately.
, = 0, 0
=
continue
+=
+= 1
return ,
Walkthrough: try wraps exactly one risky line, and empty lines are filtered out
beforehand so they do not count as skipped. For a file with the lines 10, abc,
an empty line, 20, 30 the result is (60, 1).
Problem 14. Your own exception. A function takes a square root and raises a custom error for a negative argument.
"""A negative value is not allowed."""
return
Walkthrough: inheriting from ValueError rather than directly from Exception
lets calling code catch it either narrowly (except NegativeError) or broadly
(except ValueError) — older code keeps working. The class body is a single
docstring, and that is enough.
Step-by-step plan
- Strings — problems 1–3Solve the palindrome, vowel count and initials problems; test each solution on two examples.
- Lists and slices — problems 4–6Second largest, splitting into chunks, common items in order.
- Dictionaries — problems 7–9Word frequencies, grouping by first letter, totals by category.
- Functions and generators — problems 10–12Mean without extremes, Fibonacci up to a limit, a call-counting decorator.
- Files and errors — problems 13–14Summing numbers from a file while skipping junk, and your own exception class.
Start learning this in your own space
The plan goes into your repository: tick off stages, keep notes — the change history shows how far you have come.
Check yourself
1.What is second_largest([5, 1, 5, 3, 9, 9]) from problem 4?
2.How many chunks does chunks([1, 2, 3, 4, 5, 6, 7], 3) from problem 5 return?
3.What is trimmed_mean(1, 5, 6, 7, 100) from problem 10?
4.How many Fibonacci numbers not exceeding 50 does the generator from problem 11 yield?
Sources
-
The Python TutorialThe official course: types, data structures, functions, filesfree
-
Exercism — Python trackFree Python exercises with automated tests and optional mentoringfree
-
Python built-in functionssorted, sum, set, enumerate — the building blocks of the solutions abovefree
Was this helpful?