Top 10 Python Projects for Beginners
Top 10 Python Projects for Beginners (With Source Code)
The fastest way to genuinely learn Python is to build things with it. This guide presents the top 10 Python projects for beginners — each with complete, copy-paste-ready source code, a clear explanation of how it works, and guidance on how to extend it once the core version is running.
Why Projects Are the Best Way to Learn Python
Reading documentation, watching tutorials, and completing exercises all have their place in learning Python — but none of them match the depth of understanding that comes from building something functional from scratch. Projects force you to confront real problems: how do you structure a program that does more than one thing? How do you handle input that does not match what you expected? How do you debug code that runs without errors but produces the wrong result?
These are the questions that experienced developers answer instinctively, and they only become instinctive through repeated exposure. Each project in this guide is designed to introduce a specific set of Python concepts — variables, loops, functions, file handling, APIs, object-oriented programming — in a context where those concepts have a visible, satisfying purpose. You are not learning for loops in the abstract; you are using them to build a number guessing game or process a CSV file.
The projects below are ordered by increasing complexity, beginning with programs that run in a few lines and progressing to applications that involve multiple functions, external libraries, and file operations. Working through them in order provides a natural learning progression. If a particular project feels straightforward, move to the next one — the goal is to be consistently challenged, not comfortable.
Setting Up Your Python Environment
Before running any of the code below, ensure you have a working Python environment. If you have not yet installed Python, our complete Python installation guide covers every platform in detail. For most of the projects in this list, the only requirement is a standard Python 3.x installation — no third-party libraries are needed. Projects 9 and 10 require the requests library, which can be installed by running pip install requests in your terminal.
Each code block below includes a copy button. Click it to copy the entire program to your clipboard, paste it into your code editor or IDE of choice, save the file with a .py extension, and run it from the terminal using python filename.py. Every program has been tested against Python 3.10 and above.
The 10 Best Python Projects for Beginners (With Source Code)
1. Number Guessing Game
Concepts covered: Variables, while loops, conditionals, user input, random module.
The number guessing game is one of the most effective first Python projects because it introduces four fundamental concepts in a single, immediately playable program. The computer picks a random number, the user guesses repeatedly, and the program provides higher/lower feedback until the correct answer is found. It is simple enough to understand in one reading and interesting enough to want to run more than once.
import random
def number_guessing_game():
number = random.randint(1, 100)
attempts = 0
print("Welcome to the Number Guessing Game!")
print("I'm thinking of a number between 1 and 100.\n")
while True:
try:
guess = int(input("Your guess: "))
except ValueError:
print("Please enter a valid number.\n")
continue
attempts += 1
if guess < number:
print("Too low! Try higher.\n")
elif guess > number:
print("Too high! Try lower.\n")
else:
print(f"Correct! You guessed it in {attempts} attempt(s). Well done!")
break
number_guessing_game()
To extend this: Add a difficulty setting that changes the range (1–50 for easy, 1–500 for hard), or track the player's best score across multiple rounds.
2. Simple Calculator
Concepts covered: Functions, conditionals, type conversion, input validation.
Building a calculator reinforces the habit of breaking a program into reusable functions — one of the most important structural skills in Python. This version handles addition, subtraction, multiplication, and division, including division-by-zero protection, and loops until the user chooses to exit.
def add(a, b): return a + b
def subtract(a, b): return a - b
def multiply(a, b): return a * b
def divide(a, b):
if b == 0:
return "Error: Cannot divide by zero."
return a / b
def calculator():
operations = {'+': add, '-': subtract, '*': multiply, '/': divide}
print("Simple Python Calculator")
print("Type 'quit' to exit.\n")
while True:
try:
a = input("First number: ")
if a.lower() == 'quit':
break
a = float(a)
op = input("Operator (+, -, *, /): ").strip()
if op not in operations:
print("Invalid operator. Please use +, -, *, or /.\n")
continue
b = float(input("Second number: "))
result = operations[op](a, b)
print(f"Result: {a} {op} {b} = {result}\n")
except ValueError:
print("Invalid input. Please enter numeric values.\n")
calculator()
To extend this: Add support for percentages, square roots, or a history log that records all calculations during the session.
3. To-Do List Application
Concepts covered: Lists, loops, functions, string formatting, basic CRUD operations.
A to-do list introduces the concept of managing a collection of data through a persistent program loop. This version allows the user to add tasks, view all tasks with numbered indices, mark tasks as complete, and delete them. It demonstrates how a simple list can serve as an in-memory database.
def show_tasks(tasks):
if not tasks:
print(" No tasks yet.\n")
return
for i, task in enumerate(tasks, 1):
status = "[x]" if task['done'] else "[ ]"
print(f" {i}. {status} {task['name']}")
print()
def todo_app():
tasks = []
print("Python To-Do List — Commands: add, view, done, delete, quit\n")
while True:
command = input("Command: ").strip().lower()
if command == "add":
name = input("Task name: ").strip()
if name:
tasks.append({'name': name, 'done': False})
print(f" Added: '{name}'\n")
elif command == "view":
show_tasks(tasks)
elif command == "done":
show_tasks(tasks)
try:
idx = int(input("Mark task number as done: ")) - 1
if 0 <= idx < len(tasks):
tasks[idx]['done'] = True
print(f" Marked '{tasks[idx]['name']}' as complete.\n")
else:
print(" Invalid task number.\n")
except ValueError:
print(" Please enter a number.\n")
elif command == "delete":
show_tasks(tasks)
try:
idx = int(input("Delete task number: ")) - 1
if 0 <= idx < len(tasks):
removed = tasks.pop(idx)
print(f" Deleted: '{removed['name']}'\n")
else:
print(" Invalid task number.\n")
except ValueError:
print(" Please enter a number.\n")
elif command == "quit":
print("Goodbye!")
break
else:
print(" Unknown command. Try: add, view, done, delete, quit\n")
todo_app()
To extend this: Save tasks to a JSON file so they persist between sessions, or add due dates and a sort-by-deadline feature.
4. Password Generator
Concepts covered: String manipulation, random module, function parameters, list comprehension.
A password generator is a practical utility that most users will actually want to run repeatedly. This version lets the user specify the desired length and choose whether to include uppercase letters, digits, and special characters, then generates a cryptographically shuffled password meeting those requirements.
import random
import string
def generate_password(length=16, use_upper=True, use_digits=True, use_symbols=True):
chars = list(string.ascii_lowercase)
guaranteed = []
if use_upper:
chars += list(string.ascii_uppercase)
guaranteed.append(random.choice(string.ascii_uppercase))
if use_digits:
chars += list(string.digits)
guaranteed.append(random.choice(string.digits))
if use_symbols:
chars += list(string.punctuation)
guaranteed.append(random.choice(string.punctuation))
remaining_length = length - len(guaranteed)
if remaining_length < 0:
remaining_length = 0
password_chars = guaranteed + [random.choice(chars) for _ in range(remaining_length)]
random.shuffle(password_chars)
return ''.join(password_chars)
def password_generator_app():
print("Python Password Generator\n")
try:
length = int(input("Password length (default 16): ") or 16)
except ValueError:
length = 16
upper = input("Include uppercase letters? (y/n, default y): ").strip().lower() != 'n'
digits = input("Include digits? (y/n, default y): ").strip().lower() != 'n'
symbols = input("Include symbols? (y/n, default y): ").strip().lower() != 'n'
password = generate_password(length, upper, digits, symbols)
print(f"\nGenerated password: {password}\n")
again = input("Generate another? (y/n): ").strip().lower()
if again == 'y':
password_generator_app()
password_generator_app()
To extend this: Generate a batch of five passwords at once, or score the strength of an existing password the user provides.
5. Countdown Timer
Concepts covered: Time module, loops, string formatting, divmod.
A countdown timer introduces the time module and the technique of updating terminal output in place — a useful pattern for any program that needs to display live progress. This version accepts a duration in seconds and displays a live HH:MM:SS countdown, printing a completion alert when the timer reaches zero.
import time
import sys
def countdown_timer(seconds):
print(f"Timer set for {seconds} second(s).\n")
try:
while seconds >= 0:
mins, secs = divmod(seconds, 60)
hours, mins = divmod(mins, 60)
timer_display = f"\r Time remaining: {hours:02d}:{mins:02d}:{secs:02d} "
sys.stdout.write(timer_display)
sys.stdout.flush()
time.sleep(1)
seconds -= 1
print("\n\n Time's up!")
except KeyboardInterrupt:
print("\n\n Timer cancelled.")
def timer_app():
print("Python Countdown Timer\n")
try:
duration = int(input("Enter duration in seconds: "))
if duration <= 0:
print("Please enter a positive number.")
return
countdown_timer(duration)
except ValueError:
print("Invalid input. Please enter a whole number of seconds.")
timer_app()
To extend this: Accept time input in MM:SS format, play a sound alert using the playsound library, or add a pause and resume feature.
6. Quiz Application
Concepts covered: Dictionaries, lists, loops, score tracking, random shuffling.
A quiz application is one of the most versatile beginner projects because the same structure can be repurposed for any subject matter. This version stores questions and answers in a list of dictionaries, shuffles them on each run to prevent memorisation, tracks the score, and provides a summary at the end.
import random
questions = [
{"question": "What keyword is used to define a function in Python?",
"options": ["A) func", "B) def", "C) define", "D) function"], "answer": "B"},
{"question": "Which data type is immutable in Python?",
"options": ["A) list", "B) dict", "C) tuple", "D) set"], "answer": "C"},
{"question": "What does len() return?",
"options": ["A) Last element", "B) Number of items", "C) First element", "D) Data type"], "answer": "B"},
{"question": "Which symbol is used for single-line comments in Python?",
"options": ["A) //", "B) /* */", "C) #", "D) --"], "answer": "C"},
{"question": "What is the output of type(3.14)?",
"options": ["A) int", "B) str", "C) number", "D) float"], "answer": "D"},
]
def run_quiz(questions):
random.shuffle(questions)
score = 0
total = len(questions)
print("Python Quiz — Answer with A, B, C, or D.\n")
for i, q in enumerate(questions, 1):
print(f"Q{i}: {q['question']}")
for option in q['options']:
print(f" {option}")
answer = input("Your answer: ").strip().upper()
if answer == q['answer']:
print(" Correct!\n")
score += 1
else:
print(f" Wrong. The correct answer was {q['answer']}.\n")
percentage = (score / total) * 100
print(f"Quiz complete! Score: {score}/{total} ({percentage:.0f}%)")
if percentage == 100:
print("Perfect score! Excellent work.")
elif percentage >= 60:
print("Good effort — review the ones you missed.")
else:
print("Keep practising — you'll get there.")
run_quiz(questions)
To extend this: Load questions from a JSON or CSV file, add a timer per question, or implement difficulty levels.
7. Contact Book
Concepts covered: Dictionaries, file handling, JSON, CRUD operations, error handling.
The contact book project introduces persistent storage — saving data to a file so it survives between program runs. This is a critical step beyond in-memory programs and mirrors the basic architecture of any real-world data application. Contacts are stored as a JSON file and loaded on startup.
import json
import os
DATA_FILE = "contacts.json"
def load_contacts():
if os.path.exists(DATA_FILE):
with open(DATA_FILE, 'r') as f:
return json.load(f)
return {}
def save_contacts(contacts):
with open(DATA_FILE, 'w') as f:
json.dump(contacts, f, indent=2)
def contact_book():
contacts = load_contacts()
print("Python Contact Book — Commands: add, view, search, delete, quit\n")
while True:
command = input("Command: ").strip().lower()
if command == "add":
name = input("Name: ").strip()
phone = input("Phone: ").strip()
email = input("Email: ").strip()
contacts[name.lower()] = {"name": name, "phone": phone, "email": email}
save_contacts(contacts)
print(f" Contact '{name}' saved.\n")
elif command == "view":
if not contacts:
print(" No contacts saved.\n")
else:
for c in contacts.values():
print(f" {c['name']} | {c['phone']} | {c['email']}")
print()
elif command == "search":
query = input("Search name: ").strip().lower()
results = [c for k, c in contacts.items() if query in k]
if results:
for c in results:
print(f" {c['name']} | {c['phone']} | {c['email']}")
print()
else:
print(" No matching contacts found.\n")
elif command == "delete":
name = input("Name to delete: ").strip().lower()
if name in contacts:
del contacts[name]
save_contacts(contacts)
print(f" Deleted contact '{name}'.\n")
else:
print(" Contact not found.\n")
elif command == "quit":
print("Contact book saved. Goodbye!")
break
else:
print(" Unknown command.\n")
contact_book()
To extend this: Add support for multiple phone numbers per contact, export contacts to CSV, or add a birthday field with upcoming-birthday alerts.
8. Expense Tracker
Concepts covered: CSV file handling, data aggregation, datetime module, formatted output.
The expense tracker builds on file handling skills by working with CSV data — the most common format for financial and tabular data in the real world. This project records expenses with categories and dates, calculates totals per category, and displays a formatted summary report.
import csv
import os
from datetime import date
from collections import defaultdict
FILE = "expenses.csv"
HEADERS = ["date", "category", "description", "amount"]
def load_expenses():
if not os.path.exists(FILE):
return []
with open(FILE, newline='') as f:
return list(csv.DictReader(f))
def save_expense(entry):
file_exists = os.path.exists(FILE)
with open(FILE, 'a', newline='') as f:
writer = csv.DictWriter(f, fieldnames=HEADERS)
if not file_exists:
writer.writeheader()
writer.writerow(entry)
def show_summary(expenses):
if not expenses:
print(" No expenses recorded yet.\n")
return
totals = defaultdict(float)
for e in expenses:
totals[e['category']] += float(e['amount'])
grand_total = sum(totals.values())
print("\n --- Expense Summary ---")
for cat, total in sorted(totals.items()):
print(f" {cat:<20 total:="">8.2f}")
print(f" {'TOTAL':<20 grand_total:="">8.2f}\n")
def expense_tracker():
print("Python Expense Tracker — Commands: add, view, summary, quit\n")
while True:
command = input("Command: ").strip().lower()
if command == "add":
category = input("Category (e.g. Food, Travel): ").strip()
description = input("Description: ").strip()
try:
amount = float(input("Amount: £"))
except ValueError:
print(" Invalid amount.\n")
continue
entry = {
"date": str(date.today()),
"category": category,
"description": description,
"amount": f"{amount:.2f}"
}
save_expense(entry)
print(f" Expense of £{amount:.2f} saved under '{category}'.\n")
elif command == "view":
expenses = load_expenses()
if not expenses:
print(" No expenses yet.\n")
else:
print(f"\n {'Date':<12 ategory="" escription="" mount="">8}")
print(" " + "-" * 62)
for e in expenses:
print(f" {e['date']:<12 amount="" category="" description="" e="" float="">7.2f}")
print()
elif command == "summary":
show_summary(load_expenses())
elif command == "quit":
print("Goodbye!")
break
else:
print(" Unknown command.\n")
expense_tracker()12>12>20>20>
To extend this: Filter expenses by month, add a monthly budget cap with alerts, or generate a bar chart using matplotlib.
9. Weather App (Using a Public API)
Concepts covered: HTTP requests, JSON parsing, API keys, error handling, requests library.
This project introduces one of the most important real-world Python skills: consuming a REST API. The weather app fetches live weather data from the Open-Meteo API — which is free and requires no API key — and displays the current temperature, wind speed, and weather condition for any city. It uses the requests library and a geocoding call to convert city names to coordinates.
Prerequisite: Run pip install requests before executing this project.
import requests
WEATHER_CODES = {
0: "Clear sky", 1: "Mainly clear", 2: "Partly cloudy", 3: "Overcast",
45: "Foggy", 48: "Icy fog", 51: "Light drizzle", 53: "Moderate drizzle",
61: "Slight rain", 63: "Moderate rain", 65: "Heavy rain",
71: "Slight snow", 73: "Moderate snow", 75: "Heavy snow",
80: "Slight showers", 81: "Moderate showers", 82: "Violent showers",
95: "Thunderstorm", 99: "Thunderstorm with hail"
}
def get_coordinates(city):
url = "https://geocoding-api.open-meteo.com/v1/search"
response = requests.get(url, params={"name": city, "count": 1, "language": "en"}, timeout=10)
response.raise_for_status()
results = response.json().get("results")
if not results:
return None, None, None
r = results[0]
return r["latitude"], r["longitude"], r["name"] + ", " + r.get("country", "")
def get_weather(lat, lon):
url = "https://api.open-meteo.com/v1/forecast"
params = {
"latitude": lat, "longitude": lon,
"current_weather": True,
"wind_speed_unit": "mph"
}
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
return response.json().get("current_weather", {})
def weather_app():
print("Python Weather App (Powered by Open-Meteo — no API key required)\n")
while True:
city = input("Enter city name (or 'quit'): ").strip()
if city.lower() == 'quit':
break
try:
lat, lon, location_name = get_coordinates(city)
if lat is None:
print(f" Could not find '{city}'. Try a different spelling.\n")
continue
weather = get_weather(lat, lon)
code = weather.get("weathercode", -1)
condition = WEATHER_CODES.get(code, "Unknown conditions")
print(f"\n Location : {location_name}")
print(f" Condition : {condition}")
print(f" Temperature: {weather.get('temperature', 'N/A')}°C")
print(f" Wind Speed : {weather.get('windspeed', 'N/A')} mph\n")
except requests.exceptions.RequestException as e:
print(f" Network error: {e}\n")
weather_app()
To extend this: Add a 7-day forecast display, include humidity and UV index, or save favourite cities for quick lookup.
10. Web Scraper — News Headlines
Concepts covered: HTTP requests, HTML parsing, BeautifulSoup, data extraction, error handling.
Web scraping is one of the most commercially valuable Python skills — it powers price monitoring tools, research aggregators, and data pipelines across the industry. This project uses the requests library alongside the free beautifulsoup4 parser to fetch and extract headlines from Hacker News, one of the cleanest and most scraping-friendly public pages available.
Prerequisite: Run pip install requests beautifulsoup4 before executing this project.
import requests
from bs4 import BeautifulSoup
def scrape_hacker_news(pages=1):
all_stories = []
print(f"Fetching top stories from Hacker News ({pages} page(s))...\n")
for page in range(1, pages + 1):
url = f"https://news.ycombinator.com/news?p={page}"
try:
response = requests.get(url, timeout=10,
headers={"User-Agent": "Mozilla/5.0"})
response.raise_for_status()
except requests.exceptions.RequestException as e:
print(f" Error fetching page {page}: {e}")
continue
soup = BeautifulSoup(response.text, 'html.parser')
titles = soup.select('.titleline > a')
scores = soup.select('.score')
for i, title_tag in enumerate(titles):
title = title_tag.get_text()
link = title_tag.get('href', '')
score = scores[i].get_text() if i < len(scores) else "N/A"
all_stories.append({"title": title, "link": link, "score": score})
return all_stories
def display_stories(stories):
if not stories:
print(" No stories retrieved.")
return
print(f" {'#':<4 1="" 80="" core="" enumerate="" for="" i="" if="" in="" len="" point="" points="" print="" pt="" pts="" replace="" score="" score_display="story[" stories="" story="" title="" title_truncated="story["> 65 else story['title']
print(f" {i:<4 1="" 3="" as="" ave="" code="" def="" default="" display_stories="" encoding="utf-8" except="" f.write="" f:="" f="" fetch="" for="" hacker="" headlines.txt="" headlines="" if="" in="" link="" lower="" many="" min="" n="" news="" news_scraper="" open="" or="" ow="" pages="1" print="" s="" save="=" saved="" score="" score_display:="" scraper="" stories:="" stories="" strip="" title="" title_truncated="" to="" try:="" valueerror:="" w="" with="" y="" ython="">4>4>
To extend this: Add filtering by keyword, schedule the scraper to run hourly using schedule, or scrape additional fields such as comment counts and author names.
What to Build Next
Having worked through these ten projects, you will have solid hands-on experience with Python's core libraries, file handling, API consumption, data structures, and error handling. The natural next steps depend on the direction that interests you most. For automation and scripting, explore combining the patterns from the expense tracker and weather app into scripts that run automatically on a schedule. For web development, the Flask and Django frameworks build directly on the Python fundamentals you have practised here. For data science, the pandas and matplotlib libraries open up data analysis workflows that extend naturally from projects like the expense tracker. Our Python for automation guide covers the next layer of complexity in practical detail.
Common Mistakes Beginners Make on Their First Projects
Trying to build everything at once. The most consistent mistake beginners make is attempting to add features before the core program works correctly. Build the simplest version that functions end-to-end first, then extend it. This approach — sometimes called iterative development — produces working software at every stage rather than incomplete software at all stages.
Not reading error messages. Python's error messages are among the most informative of any programming language. When a program fails, the traceback tells you the exact file, line number, and type of error. Most beginners search immediately for external solutions without reading the message carefully. Developing the habit of reading the full traceback before anything else will save significant debugging time.
Copying code without understanding it. The projects in this guide are designed to be read and understood, not merely executed. If a line is unclear, look up the relevant documentation — Python's official docs at docs.python.org are well-written and accessible — or add a print() statement to inspect what a variable contains at that point in the program. Understanding is what converts copied code into transferable skill.
Skipping input validation. Every project above includes input validation — checking that the user's input is the expected type and within acceptable bounds before using it. Beginners often skip this, producing programs that crash on unexpected input. Handling bad input gracefully is one of the clearest markers of a developer who thinks beyond the happy path. The Real Python guides on error handling and exceptions are an excellent next step for deepening this skill.
Extending Your Projects: Taking Them Further
Each of the ten projects above is a functional baseline — a program that works correctly but represents only a fraction of what it could become. The most productive learning happens in the extension phase, when you move from following instructions to making your own design decisions.
A productive extension strategy is to pick two or three projects from this list that genuinely interest you and spend a week on each, implementing one or two of the suggested extensions. The contact book becomes a significantly more interesting and technically challenging project once you add CSV export, birthday tracking, and input validation for phone number formats. The quiz application becomes a genuinely useful study tool once it loads questions from an external file and tracks performance over multiple sessions. The news scraper becomes commercially useful once it filters by keyword, sends email alerts, and runs on a schedule without manual intervention.
The transition from completing a project to extending it is the transition from following tutorials to writing original software — and that transition is the most important threshold in a developer's early learning journey. It is where Python stops being something you are learning and starts being something you are using.
Frequently Asked Questions
What Python version do I need to run these projects?
All ten projects are written for Python 3.10 and above. Python 3.6 or later will run most of them, but f-strings (used throughout for output formatting) require Python 3.6 as a minimum, and some type annotations in later projects are cleaner on 3.10+. If you are unsure which version you have, run python --version in your terminal. If you need to install or update Python, our Python installation guide covers every platform step by step.
Which IDE or code editor should I use for these projects?
VS Code with the Python extension installed is the most popular choice and is well-suited to all ten projects here. PyCharm Community Edition is an excellent alternative with stronger built-in Python tooling. For the earlier, shorter projects, even a basic text editor and a terminal window is perfectly adequate. The most important thing is to run each project from the command line rather than within a notebook environment, as several projects use interactive input loops that behave differently in Jupyter.
Do any of these projects require an internet connection?
Projects 1 through 8 run entirely offline. Projects 9 and 10 require an internet connection — the weather app calls the Open-Meteo API and the news scraper fetches from Hacker News. Both services are free and require no registration or API key.
How long should each project take to complete?
Projects 1 through 4 should take between 30 minutes and two hours for a complete beginner — longer if you spend time reading the documentation for each new concept encountered. Projects 5 through 8 typically take two to four hours. Projects 9 and 10 may take a full day if you are new to APIs and HTML parsing respectively. These estimates assume you are reading and understanding the code rather than simply copying and running it.
Conclusion
The ten Python projects in this guide span the core skills that define a competent Python developer: control flow, data structures, file handling, API consumption, web scraping, and structured program design. Working through them in order provides a natural and progressive path from writing your first loop to building programs that interact with the live internet.
Every project includes complete, copy-paste-ready source code — but the code is a starting point, not a finish line. The real learning happens when you modify it, break it, fix it, and extend it beyond what is written here. That process of iteration is what Python development actually looks like in practice, and beginning it now, on small projects where the stakes are low, is the best possible preparation for larger and more complex work ahead.
Share this guide with a developer just beginning their Python journey, bookmark it as a reference, and explore the rest of Verxio for deeper coverage of Python automation, Linux, and cybersecurity.




