
Python Practice Problems with Solutions
Welcome, Python enthusiast! Whether you're just starting or looking to sharpen your coding skills, practice problems are one of the best ways to reinforce your learning. In this article, I've compiled a variety of Python problems ranging from beginner to intermediate level, complete with solutions and explanations. Let's dive right in!
Basic Problems for Beginners
Let's start with some fundamental problems that will help you get comfortable with Python syntax and basic operations. These exercises focus on variables, data types, loops, and conditional statements.
Problem: Write a function that checks if a number is even or odd.
def check_even_odd(number):
if number % 2 == 0:
return "Even"
else:
return "Odd"
# Test the function
print(check_even_odd(4)) # Output: Even
print(check_even_odd(7)) # Output: Odd
Problem: Create a function that returns the factorial of a number.
def factorial(n):
result = 1
for i in range(1, n + 1):
result *= i
return result
# Test the function
print(factorial(5)) # Output: 120
Here's a simple table showing factorial values for numbers 1 through 5:
Number | Factorial |
---|---|
1 | 1 |
2 | 2 |
3 | 6 |
4 | 24 |
5 | 120 |
Problem: Write a program that prints all prime numbers between 1 and 100.
def is_prime(num):
if num < 2:
return False
for i in range(2, int(num ** 0.5) + 1):
if num % i == 0:
return False
return True
primes = [num for num in range(1, 101) if is_prime(num)]
print(primes)
When working with these basic problems, remember these key points: - Always test your functions with different inputs - Pay attention to edge cases (like 0 or negative numbers) - Use meaningful variable names - Comment your code to explain your logic
String Manipulation Problems
Strings are fundamental in Python, and mastering string operations is crucial for many programming tasks. Let's explore some common string problems.
Problem: Write a function that reverses a string.
def reverse_string(s):
return s[::-1]
# Test the function
print(reverse_string("hello")) # Output: olleh
Problem: Create a function that checks if a string is a palindrome.
def is_palindrome(s):
s = s.lower().replace(" ", "")
return s == s[::-1]
# Test the function
print(is_palindrome("madam")) # Output: True
print(is_palindrome("python")) # Output: False
Problem: Write a function that counts the occurrences of each character in a string.
def count_characters(s):
char_count = {}
for char in s:
char_count[char] = char_count.get(char, 0) + 1
return char_count
# Test the function
print(count_characters("hello")) # Output: {'h': 1, 'e': 1, 'l': 2, 'o': 1}
Here's what you should keep in mind when working with strings: - Python strings are immutable - Use string methods like lower(), upper(), strip() effectively - String slicing is a powerful feature - Regular expressions can be useful for complex patterns
String Operation | Method Example | Result |
---|---|---|
Convert to uppercase | "hello".upper() | "HELLO" |
Replace substring | "hello".replace("l", "x") | "hexxo" |
Split string | "a,b,c".split(",") | ["a", "b", "c"] |
List and Dictionary Problems
Lists and dictionaries are among the most commonly used data structures in Python. Let's practice some problems that will help you master them.
Problem: Write a function that removes duplicates from a list.
def remove_duplicates(lst):
return list(set(lst))
# Test the function
print(remove_duplicates([1, 2, 2, 3, 4, 4, 5])) # Output: [1, 2, 3, 4, 5]
Problem: Create a function that finds the most frequent element in a list.
def most_frequent(lst):
from collections import Counter
return Counter(lst).most_common(1)[0][0]
# Test the function
print(most_frequent([1, 2, 2, 3, 3, 3, 4])) # Output: 3
Problem: Write a function that merges two dictionaries.
def merge_dicts(dict1, dict2):
return {**dict1, **dict2}
# Test the function
dict_a = {'a': 1, 'b': 2}
dict_b = {'c': 3, 'd': 4}
print(merge_dicts(dict_a, dict_b)) # Output: {'a': 1, 'b': 2, 'c': 3, 'd': 4}
When working with lists and dictionaries, consider these best practices: - Use list comprehensions for concise code - Understand time complexity of different operations - Use built-in methods like sort(), reverse(), and get() - Consider using collections module for advanced data structures
File Handling Problems
Working with files is an essential skill for any Python developer. Let's look at some common file operations.
Problem: Write a function that counts the number of lines in a file.
def count_lines(filename):
with open(filename, 'r') as file:
return len(file.readlines())
# Usage example
print(count_lines('sample.txt'))
Problem: Create a function that reads a CSV file and returns the data as a list of dictionaries.
import csv
def read_csv_to_dict(filename):
with open(filename, 'r') as file:
reader = csv.DictReader(file)
return list(reader)
# Usage example
data = read_csv_to_dict('data.csv')
Problem: Write a function that finds the longest word in a text file.
def longest_word_in_file(filename):
with open(filename, 'r') as file:
words = file.read().split()
return max(words, key=len)
# Usage example
print(longest_word_in_file('text.txt'))
Here are some important file handling concepts: - Always use context managers (with statements) - Handle exceptions when working with files - Close files properly to avoid resource leaks - Use appropriate file modes ('r', 'w', 'a', etc.)
File Mode | Description | Use Case |
---|---|---|
'r' | Read mode | Reading existing files |
'w' | Write mode | Creating new files or overwriting existing ones |
'a' | Append mode | Adding to existing files |
'r+' | Read and write | Both reading and writing to existing files |
Object-Oriented Programming Problems
Object-oriented programming is a fundamental paradigm in Python. Let's practice some OOP concepts.
Problem: Create a BankAccount class with deposit and withdraw methods.
class BankAccount:
def __init__(self, balance=0):
self.balance = balance
def deposit(self, amount):
self.balance += amount
return self.balance
def withdraw(self, amount):
if amount > self.balance:
raise ValueError("Insufficient funds")
self.balance -= amount
return self.balance
# Test the class
account = BankAccount(100)
print(account.deposit(50)) # Output: 150
print(account.withdraw(30)) # Output: 120
Problem: Create a Rectangle class with area and perimeter methods.
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
def area(self):
return self.length * self.width
def perimeter(self):
return 2 * (self.length + self.width)
# Test the class
rect = Rectangle(5, 3)
print(rect.area()) # Output: 15
print(rect.perimeter()) # Output: 16
Problem: Implement inheritance with a base Animal class and derived classes.
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError("Subclass must implement this method")
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
# Test the classes
dog = Dog("Buddy")
cat = Cat("Whiskers")
print(dog.speak()) # Output: Woof!
print(cat.speak()) # Output: Meow!
When working with OOP in Python, remember: - Use encapsulation to protect data - Inheritance allows code reuse - Polymorphism enables flexible code - Understand the difference between class and instance variables
Algorithmic Problems
Now let's tackle some algorithmic problems that will help you think like a programmer and improve your problem-solving skills.
Problem: Implement binary search on a sorted list.
def binary_search(arr, target):
low, high = 0, len(arr) - 1
while low <= high:
mid = (low + high) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
low = mid + 1
else:
high = mid - 1
return -1
# Test the function
sorted_list = [1, 3, 5, 7, 9, 11, 13]
print(binary_search(sorted_list, 9)) # Output: 4
print(binary_search(sorted_list, 6)) # Output: -1
Problem: Write a function to sort a list using bubble sort.
def bubble_sort(arr):
n = len(arr)
for i in range(n):
for j in range(0, n - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
return arr
# Test the function
unsorted_list = [64, 34, 25, 12, 22, 11, 90]
print(bubble_sort(unsorted_list)) # Output: [11, 12, 22, 25, 34, 64, 90]
Problem: Implement the Fibonacci sequence using recursion and memoization.
def fibonacci(n, memo={}):
if n in memo:
return memo[n]
if n <= 2:
return 1
memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo)
return memo[n]
# Test the function
print(fibonacci(10)) # Output: 55
Here's a comparison of different sorting algorithms:
Algorithm | Time Complexity (Best) | Time Complexity (Worst) | Space Complexity |
---|---|---|---|
Bubble Sort | O(n) | O(n²) | O(1) |
Selection Sort | O(n²) | O(n²) | O(1) |
Insertion Sort | O(n) | O(n²) | O(1) |
Merge Sort | O(n log n) | O(n log n) | O(n) |
Quick Sort | O(n log n) | O(n²) | O(log n) |
When working with algorithms: - Understand time and space complexity - Choose the right algorithm for your use case - Test with different input sizes - Consider edge cases and worst-case scenarios
Real-World Application Problems
Let's look at some problems that simulate real-world scenarios you might encounter as a Python developer.
Problem: Create a simple weather data analyzer.
def analyze_weather_data(temperatures):
analysis = {
'max_temp': max(temperatures),
'min_temp': min(temperatures),
'avg_temp': sum(temperatures) / len(temperatures),
'temperature_range': max(temperatures) - min(temperatures)
}
return analysis
# Test the function
temps = [22, 24, 19, 21, 25, 23, 20]
print(analyze_weather_data(temps))
Problem: Build a simple expense tracker.
class ExpenseTracker:
def __init__(self):
self.expenses = []
def add_expense(self, amount, category, description):
self.expenses.append({
'amount': amount,
'category': category,
'description': description
})
def total_expenses(self):
return sum(expense['amount'] for expense in self.expenses)
def expenses_by_category(self):
categories = {}
for expense in self.expenses:
categories[expense['category']] = categories.get(expense['category'], 0) + expense['amount']
return categories
# Test the class
tracker = ExpenseTracker()
tracker.add_expense(15.50, 'Food', 'Lunch')
tracker.add_expense(30.00, 'Transport', 'Bus fare')
tracker.add_expense(8.75, 'Food', 'Coffee')
print(tracker.total_expenses()) # Output: 54.25
print(tracker.expenses_by_category()) # Output: {'Food': 24.25, 'Transport': 30.00}
Problem: Create a password strength checker.
def check_password_strength(password):
strength = 0
feedback = []
if len(password) >= 8:
strength += 1
else:
feedback.append("Password should be at least 8 characters long")
if any(char.isdigit() for char in password):
strength += 1
else:
feedback.append("Password should contain at least one digit")
if any(char.isupper() for char in password):
strength += 1
else:
feedback.append("Password should contain at least one uppercase letter")
if any(char.islower() for char in password):
strength += 1
else:
feedback.append("Password should contain at least one lowercase letter")
if any(not char.isalnum() for char in password):
strength += 1
else:
feedback.append("Password should contain at least one special character")
return strength, feedback
# Test the function
strength, feedback = check_password_strength("Weak1")
print(f"Strength: {strength}/5")
print("Feedback:", feedback)
When working on real-world problems: - Consider user experience and error handling - Write modular, maintainable code - Add documentation and comments - Test with realistic data
Debugging and Optimization Problems
Let's practice some problems that will help you improve your debugging skills and write more efficient code.
Problem: Identify and fix the bug in this code.
# Buggy code
def calculate_average(numbers):
total = 0
for number in numbers:
total += number
return total / len(numbers)
# Fixed code
def calculate_average(numbers):
if not numbers:
return 0 # Handle empty list
total = 0
for number in numbers:
total += number
return total / len(numbers)
# Test the function
print(calculate_average([1, 2, 3, 4, 5])) # Output: 3.0
print(calculate_average([])) # Output: 0 (instead of crashing)
Problem: Optimize this inefficient code.
# Inefficient version
def find_duplicates(lst):
duplicates = []
for i in range(len(lst)):
for j in range(i + 1, len(lst)):
if lst[i] == lst[j] and lst[i] not in duplicates:
duplicates.append(lst[i])
return duplicates
# Optimized version
def find_duplicates(lst):
seen = set()
duplicates = set()
for item in lst:
if item in seen:
duplicates.add(item)
else:
seen.add(item)
return list(duplicates)
# Test both functions
test_list = [1, 2, 3, 2, 4, 5, 3, 6, 7, 8, 1]
print(find_duplicates(test_list)) # Output: [1, 2, 3]
Problem: Refactor this code to make it more readable and maintainable.
# Original code
def p(s):
l = 0
r = len(s) - 1
while l < r:
if s[l] != s[r]:
return False
l += 1
r -= 1
return True
# Refactored code
def is_palindrome(string):
left = 0
right = len(string) - 1
while left < right:
if string[left] != string[right]:
return False
left += 1
right -= 1
return True
# Test the function
print(is_palindrome("racecar")) # Output: True
print(is_palindrome("python")) # Output: False
Here are some debugging and optimization tips: - Use meaningful variable names - Add comments to explain complex logic - Test edge cases and boundary conditions - Use Python's built-in functions and libraries - Profile your code to identify bottlenecks
Optimization Technique | When to Use | Example |
---|---|---|
Memoization | Repeated calculations | Fibonacci sequence |
Early termination | Search algorithms | Binary search |
Using sets for membership tests | Checking for duplicates | Finding unique elements |
List comprehensions | Creating new lists | Filtering or transforming data |
Remember, the key to mastering Python is consistent practice. Work through these problems, understand the solutions, and then try to solve them in different ways. Don't just copy the solutions - type them out yourself, experiment with modifications, and challenge yourself to come up with alternative approaches.
Happy coding, and keep practicing! The more problems you solve, the more comfortable you'll become with Python programming.