5 Small Python Projects Every Beginner Should Try
Tutorials are good at showing you how Python works. They’re bad at showing you what to do when nobody tells you the next line.
That’s what small projects are for. A project hands you a goal instead of instructions. You have to decide what the program needs to remember, what it should do first, and what happens when the user types something weird. Those decisions are where most of the actual learning happens.
The key word is small. A beginner’s first project shouldn’t be a social network or a stock-trading bot. It should be something you can get working in an evening, then keep improving for a week.
The five projects below are ordered on purpose. Each one adds one or two new ideas on top of the last:
- Number guessing game: variables, loops, conditions
- Expense tracker: lists, dictionaries, functions, saving to a file
- Password generator: strings, the standard library, input checks
- Weather app: calling a real API and reading JSON
- Downloads folder organizer: working with files and folders on your own computer
Each one comes with a short starter version (it runs on Python 3.10 or newer), the concepts it teaches, a mistake beginners often make, and a few ways to stretch it once the basic version works.
One rule before you start: type the code yourself instead of copying it. Then change something and see what breaks. You’ll learn more from the change than from the copy.
1. Number guessing game
The computer picks a number from 1 to 100. You guess. It tells you “too high” or “too low” until you get it.
It sounds almost too simple, but it covers the core of every program: a value the program remembers (the secret number), a loop that keeps going until something happens, and decisions based on what the user typed.
import randomsecret = random.randint(1, 100)
attempts = 0while True:
guess = input("Guess a number from 1 to 100: ")
if not guess.isdigit():
print("Please type a whole number.")
continue guess = int(guess)
attempts += 1 if guess < secret:
print("Too low.")
elif guess > secret:
print("Too high.")
else:
print(f"Got it in {attempts} tries!")
break
What it teaches: variables, while loops, if/elif/else, break and continue, and importing your first module.
The common mistake: forgetting that input() always returns text. Compare "50" < 70 and Python raises a TypeError. That's why the code checks isdigit() first and converts with int() afterward. Delete the check, type "fifty", and watch what happens. It's a useful crash to see once.
Ways to stretch it:
- Limit the player to 7 attempts. (With 1 to 100, a player who always guesses the middle never needs more than 7.)
- Add difficulty levels that change the range.
- Ask “Play again?” at the end and keep a best score for the session.
- Flip it around: you pick the number and the computer guesses.
That last one is sneakily good practice. To make the computer guess well, you’ll end up writing binary search without being told to.
2. Expense tracker
A small command-line app where you add expenses with a category and an amount, then see the total per category. The data gets saved to a CSV file, so it’s still there the next time you run the program.
This is the first project where the code gets long enough that you need functions to keep it readable. It’s also the first time your program remembers anything after it closes.
import csv
import osFILE = "expenses.csv"def load_expenses():
if not os.path.exists(FILE):
return []
with open(FILE, newline="") as f:
return list(csv.DictReader(f))def save_expense(expense):
is_new = not os.path.exists(FILE)
with open(FILE, "a", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["category", "amount"])
if is_new:
writer.writeheader()
writer.writerow(expense)def show_summary(expenses):
totals = {}
for e in expenses:
totals[e["category"]] = totals.get(e["category"], 0) + float(e["amount"])
for category, total in totals.items():
print(f"{category}: {total:.2f}")while True:
choice = input("\n1) Add 2) Summary 3) Quit: ")
if choice == "1":
category = input("Category: ").strip().lower()
amount = input("Amount: ")
save_expense({"category": category, "amount": amount})
elif choice == "2":
show_summary(load_expenses())
elif choice == "3":
break
What it teaches: lists and dictionaries, functions with a single job each, reading and writing files, and the csv module.
Notice the .strip().lower() on the category. Without it, "Food", "food" and "food " become three separate categories in your summary. Cleaning input before you store it saves you from a lot of strange-looking output later.
The common mistake: this version trusts the amount completely. Type “twelve” as an amount and it saves fine, then the summary crashes when float() hits it. Fixing that with a try/except around the conversion is a good first exercise in error handling. The bug shows up in one function, but the right fix belongs in another.
Ways to stretch it:
- Validate amounts before saving.
- Add a date to each expense and show totals for the current month.
- Show which category takes the biggest share of your spending.
- Once you’re comfortable, swap the CSV for SQLite using Python’s built-in
sqlite3module.
Sometimes, even after practicing with small projects, a programming assignment can still feel overwhelming. When you are stuck on an unfamiliar concept, debugging problem, or project requirement, getting additional guidance can make the learning process easier. AssignmentDude can be considered as a source of programming assignment help when you need another perspective on a difficult task. The important thing is to use the assistance to understand the logic, improve your coding skills, and learn how to approach similar problems independently in the future.
3. Password generator
You choose a length, and the program builds a random password from letters, digits and symbols.
It’s short, but it introduces something beginners often skip: looking for the right tool in Python’s standard library instead of writing everything from scratch.
import secrets
import stringdef make_password(length=16):
characters = string.ascii_letters + string.digits + string.punctuation
return "".join(secrets.choice(characters) for _ in range(length))length = input("Password length (press Enter for 16): ")
length = int(length) if length.isdigit() else 16if length < 8:
print("That's too short. Use at least 8 characters.")
else:
print(make_password(length))
What it teaches: strings, default parameter values, join, generator expressions, and the difference between two modules that look like they do the same thing.
Get Smithaydon’s stories in your inbox
Join Medium for free to get updates from this writer.
That last point is the interesting one. Most beginners reach for random.choice() here. The Python documentation says the random module shouldn't be used for security purposes and points you to secrets instead, because random is built for things like games and simulations, not for values someone might try to guess. Same output on screen, very different answer to "is this safe?"
Ways to stretch it:
- Let the user switch symbols on or off, since some websites reject them.
- Guarantee at least one digit and one symbol in every password.
- Write the opposite program: a strength checker that rates a password the user types.
- Generate a passphrase from random words instead of random characters.
4. Weather app with a real API
Type a city name and get its current temperature and wind speed, pulled live from the internet.
This is usually the project where Python stops feeling like homework. Your code talks to a server somewhere else, gets real data back, and does something with it. It uses Open-Meteo, a weather API that’s free for non-commercial use and doesn’t need an API key, so there’s no sign-up step between you and the code.
Install the requests library first with pip install requests. Then:
import requestsdef get_coordinates(city):
url = "https://geocoding-api.open-meteo.com/v1/search"
response = requests.get(url, params={"name": city, "count": 1}, timeout=10)
response.raise_for_status()
results = response.json().get("results")
if not results:
return None
place = results[0]
return place["latitude"], place["longitude"], place["name"], place["country"]def get_weather(lat, lon):
url = "https://api.open-meteo.com/v1/forecast"
params = {"latitude": lat, "longitude": lon,
"current": "temperature_2m,wind_speed_10m"}
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
return response.json()["current"]city = input("City: ")
location = get_coordinates(city)if location is None:
print("Couldn't find that city.")
else:
lat, lon, name, country = location
current = get_weather(lat, lon)
print(f"{name}, {country}: {current['temperature_2m']}°C, "
f"wind {current['wind_speed_10m']} km/h")
What it teaches: installing a package with pip, making HTTP requests, reading JSON (which turns into ordinary Python dictionaries and lists), and chaining two API calls together.
That chaining is worth noticing. The weather endpoint wants coordinates, not a city name, so the program asks one API for the coordinates and passes them to the next. A lot of real software works exactly like this: small calls, each feeding the next one.
The common mistake: assuming the data will always be there. Search for a city that doesn’t exist and the response simply has no "results" key. Writing response.json()["results"] would crash with a KeyError. Using .get("results") and checking for None handles it cleanly. The timeout=10 matters too. Without it, a slow server can leave your program hanging with no error at all.
Ways to stretch it:
- Add a 3-day forecast using the API’s
dailyparameter. - Handle a lost internet connection with
try/except requests.RequestException. - Save favourite cities to a file and show all of them at once.
- Turn it into a tiny web page with Flask once you’re comfortable.
5. Downloads folder organizer
Most people’s Downloads folder is a mess of PDFs, screenshots, zip files and installers. This script sorts each file into a subfolder based on its type: Images, Documents, Archives, Code, or Other.
It’s the first project here that does something useful on your own computer, and it’s a good introduction to automation. It also comes with a real responsibility, because this code moves actual files. So it starts in dry-run mode: it prints what it would do and touches nothing.
from pathlib import Path
import shutilFOLDER = Path.home() / "Downloads"
DRY_RUN = True # change to False when the plan looks rightGROUPS = {
"Images": [".jpg", ".jpeg", ".png", ".gif", ".webp"],
"Documents": [".pdf", ".docx", ".txt", ".xlsx", ".pptx"],
"Archives": [".zip", ".rar", ".7z"],
"Code": [".py", ".js", ".html", ".css", ".ipynb"],
}def folder_for(file):
for group, extensions in GROUPS.items():
if file.suffix.lower() in extensions:
return group
return "Other"for file in FOLDER.iterdir():
if not file.is_file():
continue
target_dir = FOLDER / folder_for(file)
target = target_dir / file.name
if target.exists():
print(f"Skipping {file.name}: already exists in {target_dir.name}")
continue
print(f"{file.name} -> {target_dir.name}/")
if not DRY_RUN:
target_dir.mkdir(exist_ok=True)
shutil.move(str(file), str(target))
What it teaches: the pathlib module, looping over a folder, file extensions, a dictionary used as a lookup table, and writing code that's safe to run on real data.
Two details carry most of the safety. .lower() means photo.JPG lands in Images instead of Other. The target.exists() check means a second report.pdf gets skipped instead of overwriting the first one. Remove either line and try it on a test folder full of dummy files. You'll see why they're there.
The common mistake: testing on the real folder first. Make a practice folder, fill it with empty files named test.png, notes.pdf and so on, and point FOLDER at it until you trust the script.
Ways to stretch it:
- Rename duplicates to
report (1).pdfinstead of skipping them. - Sort by month as well as type, like
Documents/2026-10. - Write a log file of every move so you can undo them.
- Schedule it to run once a week with Task Scheduler on Windows or cron on macOS and Linux.
After the five: make them yours
Finishing the starter versions is the easy part. The projects start teaching you something once you change them in ways no tutorial planned for.
A simple way to do that: pick one stretch goal per project and build it without looking anything up for the first 20 minutes. You’ll get stuck. That’s the useful part. Being stuck is where you learn to read error messages, search for the right thing, and test one guess at a time.
If you want these to count toward a portfolio, put each one on GitHub with a short README covering three things: what it does, how to run it, and one thing you’d improve next. A recruiter or professor won’t be impressed by a guessing game on its own. They will notice a guessing game with input validation, a play-again loop, clean commits, and a README that explains the decisions you made.
Notice what these five projects quietly covered: state, control flow, functions, data structures, APIs, abstraction and debugging. If any of those still feel fuzzy, 10 Programming Concepts That Finally Make Computer Science Click walks through each one as a mental model rather than a definition.
None of these projects will impress anyone on the first day. The guessing game is about 20 lines, and the password generator is even shorter. But they’re complete. They take input, make decisions, handle mistakes and produce something real, which is everything a bigger program does, just smaller.
Which one are you going to build first?
Frequently asked questions
What is a good first Python project for a complete beginner?
A number guessing game. It’s about 20 lines and uses variables, a loop, conditions and user input, which are the building blocks of almost every program you’ll write later.
Do I need to install anything for these projects?
Just Python 3.10 or newer. The weather app also needs the requests library, which you install with pip install requests. Everything else uses modules that come with Python.
Should I use random or secrets for a password generator?
Use secrets. Python’s documentation warns that the random module shouldn’t be used for security purposes, and points to the secrets module for things like passwords and tokens.
How do I make beginner Python projects look good on a portfolio?
Add at least one feature beyond the starter version, put the code on GitHub, and write a short README that explains what the project does, how to run it, and what you’d improve next.









