Python Tutorial
Classic Python Programs for Practice
Small, classic programs are the best way to build fluency and prepare for coding interviews and exams. Each one combines basics — loops, conditions, functions, recursion, data structures — into a complete solution.
This lesson solves the most commonly asked programs, several in more than one way: Fibonacci numbers (including the nth Fibonacci number), prime checks and the Sieve of Eratosthenes, palindromes, factorial, the second largest number in a list, Tower of Hanoi, Armstrong numbers, anagrams, reversing, GCD, FizzBuzz, and a simple pattern.
How to Practise
For each program: write it yourself before reading the solution; test edge cases (empty list, 0, 1, negative numbers, duplicates); then improve it — can you make it faster, shorter, or more readable? Compare an iterative and a recursive solution where both exist.
Complexity Matters
Many of these programs have a naive solution and an efficient one: recursive Fibonacci is exponential, while the iterative version is linear; checking primes by testing every divisor is O(n), while testing up to √n is O(√n), and the Sieve finds all primes up to n in O(n log log n). Interviewers expect you to discuss this.
Examples
Fibonacci: first n numbers and the nth number (three ways)
from functools import cache
def fibonacci_series(n):
a, b, series = 0, 1, []
for _ in range(n):
series.append(a)
a, b = b, a + b
return series
def nth_fibonacci_iterative(n):
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a
@cache
def nth_fibonacci_recursive(n):
return n if n < 2 else nth_fibonacci_recursive(n - 1) + nth_fibonacci_recursive(n - 2)
def fib_generator():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
print(fibonacci_series(10))
print(nth_fibonacci_iterative(50), nth_fibonacci_recursive(50))
gen = fib_generator()
print([next(gen) for _ in range(8)])
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
12586269025 12586269025
[0, 1, 1, 2, 3, 5, 8, 13]
Primes: a single check and the Sieve of Eratosthenes
import math
def is_prime(n):
if n < 2:
return False
if n % 2 == 0:
return n == 2
return all(n % d for d in range(3, math.isqrt(n) + 1, 2))
def sieve(limit):
is_p = [True] * (limit + 1)
is_p[0:2] = [False, False]
for n in range(2, math.isqrt(limit) + 1):
if is_p[n]:
is_p[n * n::n] = [False] * len(range(n * n, limit + 1, n))
return [n for n, prime in enumerate(is_p) if prime]
print([n for n in range(20) if is_prime(n)])
print(is_prime(97), is_prime(1_000_003))
print(sieve(50))
[2, 3, 5, 7, 11, 13, 17, 19]
True True
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
Second largest number, palindromes, anagrams and reversing
def second_largest(numbers):
first = second = None
for n in numbers:
if first is None or n > first:
first, second = n, first
elif n != first and (second is None or n > second):
second = n
return second
def is_palindrome(text):
cleaned = "".join(ch.lower() for ch in text if ch.isalnum())
return cleaned == cleaned[::-1]
def are_anagrams(a, b):
return sorted(a.replace(" ", "").lower()) == sorted(b.replace(" ", "").lower())
print(second_largest([10, 45, 32, 45, 8]), second_largest([5, 5]), sorted(set([10, 45, 32, 45, 8]))[-2])
print(is_palindrome("A man, a plan, a canal: Panama"), is_palindrome("Python"), str(12321) == str(12321)[::-1])
print(are_anagrams("Listen", "Silent"), are_anagrams("Dormitory", "Dirty room"), are_anagrams("abc", "abd"))
print("Webnest"[::-1], int(str(12345)[::-1]), " ".join(reversed("learn python daily".split())))
32 None 32
True False True
True True False
tsenbeW 54321 daily python learn
Tower of Hanoi
def hanoi(n, source, target, spare, moves):
if n == 0:
return
hanoi(n - 1, source, spare, target, moves)
moves.append(f"Move disk {n} from {source} to {target}")
hanoi(n - 1, spare, target, source, moves)
moves = []
hanoi(3, "A", "C", "B", moves)
print("\n".join(moves))
print("total moves:", len(moves), "| formula 2^n - 1 =", 2 ** 3 - 1)
Move disk 1 from A to C
Move disk 2 from A to B
Move disk 1 from C to B
Move disk 3 from A to C
Move disk 1 from B to A
Move disk 2 from B to C
Move disk 1 from A to C
total moves: 7 | formula 2^n - 1 = 7
Factorial, Armstrong numbers, GCD, FizzBuzz and a pattern
import math
def factorial(n):
result = 1
for i in range(2, n + 1):
result *= i
return result
def is_armstrong(n):
digits = str(n)
return n == sum(int(d) ** len(digits) for d in digits)
def gcd(a, b):
while b:
a, b = b, a % b
return a
print(factorial(10), math.factorial(10))
print([n for n in range(1, 1000) if is_armstrong(n) and n > 9])
print(gcd(84, 36), math.gcd(84, 36))
print(" ".join("FizzBuzz" if i % 15 == 0 else "Fizz" if i % 3 == 0 else "Buzz" if i % 5 == 0 else str(i) for i in range(1, 16)))
for row in range(1, 5):
print(" " * (4 - row) + "*" * (2 * row - 1))
3628800 3628800
[153, 370, 371, 407]
12 12
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz
*
***
*****
*******
Common Mistakes
- Using plain recursive Fibonacci for large n without memoization.
- Finding the second largest with sorted(nums)[-2], which fails with duplicates of the maximum.
- Checking primes by testing all divisors up to n instead of √n.
- Ignoring case, spaces and punctuation in palindrome and anagram checks.
Key Points to Remember
- Classic programs build fluency with loops, recursion and data structures.
- Prefer efficient versions: iterative/memoized Fibonacci, √n prime checks, the Sieve.
- Handle edge cases: duplicates, empty input, 0 and 1.
- Tower of Hanoi needs 2^n − 1 moves and is the classic recursion example.
Practice the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.