The best way to learn Python isn't watching tutorials — it's building things. Here are 15 project ideas organized by difficulty, each with a description, the skills you'll learn, and starter code to get you going.
Why Build Projects?
Reading documentation and following along with videos gives you knowledge, but building projects gives you skills. Every project on this list was chosen because it teaches specific, marketable skills that employers look for.
Each project includes:
- What you'll build
- Difficulty level and estimated time
- Key skills you'll learn
- Starter code to get going
Beginner Projects (1-5)
1. Password Generator
Difficulty: ★☆☆☆☆ | Time: 1-2 hours | Skills: strings, random module, functions
Build a CLI tool that generates secure passwords with customizable length and character types.
import random
import string
def generate_password(length=16, uppercase=True, digits=True, symbols=True):
chars = string.ascii_lowercase
if uppercase: chars += string.ascii_uppercase
if digits: chars += string.digits
if symbols: chars += '!@#$%^&*()_+-='
# Ensure at least one of each required type
password = [random.choice(string.ascii_lowercase)]
if uppercase: password.append(random.choice(string.ascii_uppercase))
if digits: password.append(random.choice(string.digits))
if symbols: password.append(random.choice('!@#$%^&*()_+-='))
# Fill remaining length
password += [random.choice(chars) for _ in range(length - len(password))]
random.shuffle(password)
return ''.join(password)
# Generate 5 passwords
for i in range(5):
print(f"Password {i+1}: {generate_password(20)}")
2. Unit Converter
Difficulty: ★☆☆☆☆ | Time: 2-3 hours | Skills: dictionaries, user input, functions
# Multi-unit converter
CONVERSIONS = {
'km_to_miles': lambda x: x * 0.621371,
'miles_to_km': lambda x: x * 1.60934,
'celsius_to_fahrenheit': lambda x: (x * 9/5) + 32,
'fahrenheit_to_celsius': lambda x: (x - 32) * 5/9,
'kg_to_pounds': lambda x: x * 2.20462,
'pounds_to_kg': lambda x: x * 0.453592,
}
def convert(value, conversion_type):
if conversion_type not in CONVERSIONS:
raise ValueError(f"Unknown conversion: {conversion_type}")
return CONVERSIONS[conversion_type](value)
# Example usage
print(f"100 km = {convert(100, 'km_to_miles'):.2f} miles")
print(f"72°F = {convert(72, 'fahrenheit_to_celsius'):.1f}°C")
print(f"80 kg = {convert(80, 'kg_to_pounds'):.1f} lbs")
3. Quiz Game
Difficulty: ★★☆☆☆ | Time: 3-4 hours | Skills: lists, dictionaries, loops, file I/O
import json
import random
class QuizGame:
def __init__(self, questions):
self.questions = questions
self.score = 0
self.total = len(questions)
def play(self):
random.shuffle(self.questions)
for i, q in enumerate(self.questions, 1):
print(f"\nQuestion {i}/{self.total}: {q['question']}")
for j, opt in enumerate(q['options'], 1):
print(f" {j}. {opt}")
answer = input("Your answer (1-4): ").strip()
if answer.isdigit() and q['options'][int(answer)-1] == q['answer']:
print("✓ Correct!")
self.score += 1
else:
print(f"✗ Wrong! Answer: {q['answer']}")
print(f"\nFinal Score: {self.score}/{self.total}")
questions = [
{"question": "What is Python's creator's name?",
"options": ["Guido van Rossum", "James Gosling", "Brendan Eich", "Dennis Ritchie"],
"answer": "Guido van Rossum"},
{"question": "Which data structure uses LIFO?",
"options": ["Queue", "Stack", "Array", "Tree"],
"answer": "Stack"},
]
game = QuizGame(questions)
game.play()
4. Expense Tracker
Difficulty: ★★☆☆☆ | Time: 4-5 hours | Skills: classes, file handling, data processing
import csv
from datetime import datetime
from collections import defaultdict
class ExpenseTracker:
def __init__(self, filename='expenses.csv'):
self.filename = filename
self.expenses = self._load()
def _load(self):
try:
with open(self.filename, 'r') as f:
return list(csv.DictReader(f))
except FileNotFoundError:
return []
def add(self, amount, category, description=''):
expense = {
'date': datetime.now().strftime('%Y-%m-%d'),
'amount': f'{float(amount):.2f}',
'category': category,
'description': description
}
self.expenses.append(expense)
self._save()
print(f"Added: ${amount} in {category}")
def _save(self):
with open(self.filename, 'w', newline='') as f:
writer = csv.DictWriter(f, fieldnames=['date','amount','category','description'])
writer.writeheader()
writer.writerows(self.expenses)
def summary(self):
by_cat = defaultdict(float)
for e in self.expenses:
by_cat[e['category']] += float(e['amount'])
total = sum(by_cat.values())
print(f"\n{'Category':<20} {'Amount':>10} {'%':>6}")
print('-' * 38)
for cat, amt in sorted(by_cat.items(), key=lambda x: -x[1]):
print(f"{cat:<20} ${amt:>9.2f} {amt/total*100:>5.1f}%")
print(f"{'TOTAL':<20} ${total:>9.2f}")
tracker = ExpenseTracker()
tracker.add(45.99, 'Food', 'Groceries')
tracker.add(120.00, 'Utilities', 'Electric bill')
tracker.summary()
5. Markdown to HTML Converter
Difficulty: ★★☆☆☆ | Time: 3-4 hours | Skills: regex, string processing, file I/O
import re
def markdown_to_html(md):
lines = md.split('\n')
html_lines = []
in_code = False
for line in lines:
# Code blocks
if line.startswith('```'):
html_lines.append('</pre></code>' if in_code else '<code><pre>')
in_code = not in_code
continue
if in_code:
html_lines.append(line)
continue
# Headers
match = re.match(r'^(#{1,6})\s+(.+)', line)
if match:
level = len(match.group(1))
html_lines.append(f'<h{level}>{match.group(2)}</h{level}>')
continue
# Bold and italic
line = re.sub(r'\*\*(.+?)\*\*', r'<strong>\1</strong>', line)
line = re.sub(r'\*(.+?)\*', r'<em>\1</em>', line)
line = re.sub(r'`(.+?)`', r'<code>\1</code>', line)
# Links
line = re.sub(r'\[(.+?)\]\((.+?)\)', r'<a href="\2">\1</a>', line)
if line.strip():
html_lines.append(f'<p>{line}</p>')
return '\n'.join(html_lines)
# Test it
sample = """# Hello World
This is **bold** and *italic* text.
Check out `inline code` and [DRIXO](https://drixo.shop)
## Code Example
```
print("Hello!")
```
"""
print(markdown_to_html(sample))
Intermediate Projects (6-10)
6. URL Shortener
Difficulty: ★★★☆☆ | Time: 6-8 hours | Skills: Flask, databases, hashing
7. File Organizer
Difficulty: ★★★☆☆ | Time: 4-6 hours | Skills: os module, pathlib, automation
from pathlib import Path
import shutil
CATEGORY_MAP = {
'Images': ['.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp'],
'Documents': ['.pdf', '.doc', '.docx', '.txt', '.xlsx', '.pptx'],
'Videos': ['.mp4', '.avi', '.mkv', '.mov'],
'Audio': ['.mp3', '.wav', '.flac', '.aac'],
'Code': ['.py', '.js', '.html', '.css', '.java', '.cpp'],
'Archives': ['.zip', '.rar', '.7z', '.tar', '.gz'],
}
def organize_folder(folder_path):
folder = Path(folder_path)
moved = 0
for file in folder.iterdir():
if file.is_dir():
continue
ext = file.suffix.lower()
dest_folder = 'Other'
for category, extensions in CATEGORY_MAP.items():
if ext in extensions:
dest_folder = category
break
dest = folder / dest_folder
dest.mkdir(exist_ok=True)
shutil.move(str(file), str(dest / file.name))
moved += 1
print(f" {file.name} → {dest_folder}/")
print(f"\nOrganized {moved} files!")
# Usage: organize_folder('/path/to/downloads')
8. Weather CLI App
Difficulty: ★★★☆☆ | Time: 4-5 hours | Skills: API requests, JSON parsing, error handling
9. Web Scraper for Job Listings
Difficulty: ★★★☆☆ | Time: 6-8 hours | Skills: requests, BeautifulSoup, CSV output
10. Personal Blog Engine
Difficulty: ★★★☆☆ | Time: 8-12 hours | Skills: Flask, templates, SQLite, CRUD operations
Advanced Projects (11-15)
11. Real-time Chat Application
Difficulty: ★★★★☆ | Time: 12-16 hours | Skills: WebSockets, asyncio, frontend integration
12. Machine Learning Image Classifier
Difficulty: ★★★★☆ | Time: 10-15 hours | Skills: scikit-learn, image processing, model training
13. REST API with Authentication
Difficulty: ★★★★☆ | Time: 12-16 hours | Skills: FastAPI, JWT tokens, database models, middleware
14. Automated Testing Framework
Difficulty: ★★★★☆ | Time: 10-12 hours | Skills: pytest, mocking, fixtures, CI/CD
15. Portfolio Website Generator
Difficulty: ★★★★★ | Time: 20+ hours | Skills: Full-stack development, templating, deployment
Tips for Success
- Start small, then expand. Get the MVP working first, then add features.
- Use version control. Commit after each feature — learn Git early.
- Write README files. Document what your project does and how to run it.
- Don't copy-paste. Type every line yourself. Muscle memory matters.
- Break when stuck. Walk away for 15 minutes. Solutions come when you're relaxed.
Getting Started Template
Here's a template to start any project:
# project_name/main.py
"""
Project: [Your Project Name]
Author: [Your Name]
Date: 2026
Description: [What this project does]
"""
def main():
print("Welcome to [Project Name]!")
# Your code starts here
pass
if __name__ == '__main__':
main()
Full-Stack Developer & Technical Writer at DRIXO
Full-stack developer with 5+ years of experience in Python and JavaScript. I love breaking down complex concepts into simple, practical tutorials. When I'm not coding, you'll find me contributing to open-source projects.
Comments