Top 40 Python Projects for Programmers (Beginner to Advanced)

The best way to learn Python? Build real projects.

When you code hands-on, you retain more, stay motivated, and develop skills that matter. Instead of passively memorizing syntax, you apply what you learn right away.

The only problem, finding the right project ideas.

That’s why we put together 40+ real-world, portfolio-worthy Python projects, from beginner-friendly builds to professional-level applications. Many include step-by-step tutorials and video walkthroughs.

Let’s start coding.

How to Choose Your Next Python Project

  • Building a portfolio: pick a project that shows data flow, APIs, automation, or a deployable app, like the real estate pipeline and Streamlit dashboard.
  • Building confidence fast: pick a small tool that solves a real problem in 30 to 90 minutes, like a file organizer, unit converter, or QR generator.
  • Preparing for interviews: pair one deployable app with one classic algorithms section, then write clear READMEs and tests.

Python Portfolio Projects

Web Scraping for Python Automated Real Estate Data Pipeline with Dashboard

Difficulty: Intermediate | Estimated time: 1 to 2 hours | Skills: web scraping, automation fundamentals

Python web scraping project using Selenium to extract real estate listings and build the foundation of an automated data pipeline.

This is Part 1 of a four-part series on building a Python automated real estate data pipeline. In this stage, we focus on web scraping real estate listings using Selenium to extract key property details like prices, addresses, beds, baths, and geolocation data. The collected data is stored in a structured format, setting the foundation for analysis in the next phase.

Start the tutorial

Data Analysis for Python Automated Real Estate Data Pipeline with Dashboard

Difficulty: Intermediate | Estimated time: 1 to 2 hours | Skills: Pandas, NumPy, data cleaning, analysis

Python data analysis project cleaning and processing real estate data using Pandas and NumPy.

This is Part 2 of a four-part series. In this stage, we focus on cleaning and analyzing the scraped real estate data to prepare it for visualization and deeper insights.

Using Pandas, NumPy, Matplotlib, and Seaborn, we handle missing values, format numerical data, and explore trends in property prices, sizes, and features. This step ensures our dataset is structured, reliable, and ready for interactive visualization in Part 3.

Start the tutorial

Streamlit Dashboard for an Automated Real Estate Data Pipeline

Difficulty: Intermediate | Estimated time: 1 to 2 hours | Skills: Streamlit, dashboarding, visualization

Interactive real estate dashboard built with Streamlit, maps, and charts.

This is Part 3 of a four-part series. In this stage, we focus on building an interactive dashboard using Streamlit to visualize real estate trends dynamically.

With Streamlit, Folium, Matplotlib, and Seaborn, we create a user-friendly interface where users can filter listings by price, bedrooms, bathrooms, and square footage, explore interactive maps, and analyze price distributions and market trends.

Start the tutorial

Automating and Tracking Historical Data for Python Real Estate Data Pipeline

Difficulty: Intermediate to advanced | Estimated time: 1 to 2 hours | Skills: scheduling, historical tracking, automation

Python project automating real estate data collection, storing timestamped datasets, and integrating historical insights into a dashboard.

This is Part 4, the final installment of our four-part series. In this stage, we implement automation and historical tracking, allowing our scraper to run on a schedule and accumulate data over time.

With APScheduler, we automate scraping and analysis, store timestamped datasets, track price trends, and integrate historical filtering into our Streamlit dashboard for deeper insights.

Start the tutorial

Python File Organizer App

Difficulty: Beginner | Estimated time: 45 to 90 minutes | Skills: file I/O, automation, directories

Python file organizer app that sorts files into folders based on type.

Tired of a cluttered downloads folder? A Python file organizer is a perfect project to simplify your life while leveling up your coding skills. Using Python’s os and shutil modules, you’ll automate file sorting into categories like Images, Videos, and Documents.

Start the tutorial

Python Unit Converter App with GUI

Difficulty: Beginner | Estimated time: 60 to 120 minutes | Skills: Tkinter, functions, UI updates

Python unit converter GUI built using Tkinter with dropdown unit selection.

A Python unit converter app is an excellent project to learn GUI programming with Tkinter and practical Python skills. You’ll create a user-friendly tool to convert units like miles to kilometers or Fahrenheit to Celsius.

Start the tutorial

Python QR Code Generator App with GUI

Difficulty: Beginner | Estimated time: 45 to 90 minutes | Skills: GUIs, images, third-party libraries

Python QR code generator GUI built using Tkinter, generating QR codes for text and URLs.

A Python QR code generator is a fun, practical project that builds your skills in GUI development and image processing. Using libraries like qrcode, Tkinter, and Pillow, you’ll create an app to generate QR codes in a few clicks.

Start the tutorial

Python Image Editor App with GUI

Difficulty: Beginner to intermediate | Estimated time: 1 to 2 hours | Skills: Pillow, GUI workflows

Python image editor GUI built with Tkinter with buttons for effects, saving, and undoing changes.

A Python image editor is a practical, creative project. Using Pillow for image processing and Tkinter for GUI development, you’ll build an app that can load, edit, and save images with filters like blur and sharpen.

Start the tutorial

Python PDF Merger App with GUI

Difficulty: Beginner to intermediate | Estimated time: 1 to 2 hours | Skills: PDF tooling, OOP, GUI

Python PDF merger GUI that adds, removes, and merges PDF files.

A Python PDF merger app is a practical project that combines PyPDF2 for PDF manipulation with GUI development. With an object-oriented approach, you’ll build a tool to select, manage, and merge PDFs.

Start the tutorial

Python URL Shortener App with GUI

Difficulty: Beginner to intermediate | Estimated time: 1 to 2 hours | Skills: PyQt, APIs, UX polish

Python URL shortener GUI built with PyQt that generates short links and copies them to the clipboard.

A Python URL shortener project combines functionality with a polished GUI. Using PyQt5 for the interface and pyshorteners for URL generation, you’ll create an app that shortens long URLs and copies them to the clipboard.

Start the tutorial

Python Real-Time Error Notification App

Difficulty: Intermediate | Estimated time: 1 to 2 hours | Skills: watchdog, alerts, monitoring

Python log monitoring app that detects file changes and sends email alerts.

A Python log monitoring system automates real-time file monitoring and notifications. Using watchdog for file changes and smtplib for email alerts, you’ll build a tool that detects events in logs and notifies you.

Start the tutorial

Interactive Python Game Projects

We designed each of these interactive game projects as step-by-step tutorials, so it feels like taking a free Python course while building real skills. If you want more structured learning, our Python course roundup can help you choose the right path.

Python Hangman Game with GUI

Difficulty: Beginner | Estimated time: 1 to 2 hours | Skills: Tkinter, OOP, game logic

Python Hangman game with a Tkinter GUI.

A Python Hangman game is a fun way to learn GUI basics and structure with object-oriented programming. You can keep it simple or expand it with difficulty levels and word lists.

Start the tutorial

Tic-Tac-Toe Game with GUI

Difficulty: Beginner | Estimated time: 1 to 2 hours | Skills: PyQt, event handling, game state

Python Tic Tac Toe game with a PyQt GUI.

A Tic-Tac-Toe project helps you practice OOP, GUI design, and event handling in Python using PyQt5. You’ll end with a complete two-player game.

Start the tutorial

Blackjack Game with GUI

Difficulty: Beginner to intermediate | Estimated time: 2 to 4 hours | Skills: OOP, UI, state management

Python Blackjack game built with PyQt.

A Blackjack game combines game logic, object modeling, and UI development. It’s a solid portfolio piece because it demonstrates structure, edge cases, and UX.

Start the tutorial

Pac-Man Game

Difficulty: Beginner | Estimated time: 2 to 4 hours | Skills: Pygame, collision detection, loops

Python Pac-Man style game built using Pygame.

A Python Pac-Man game is a fun way to learn 2D game mechanics with Pygame, especially movement, collisions, and basic AI patterns.

Start the tutorial

Pong Arcade Game

Difficulty: Beginner | Estimated time: 1 to 2 hours | Skills: Turtle, input handling, collisions

Python Pong game using Turtle.

A Pong project introduces object movement, collision detection, and user input handling in a simple environment using the Turtle module.

Start the tutorial

Speed Typing Test

Difficulty: Beginner | Estimated time: 1 to 2 hours | Skills: Tkinter, validation, timing

Python speed typing test app built with Tkinter.

A speed typing test is a fun project to practice GUI development, input validation, and time-based calculations in an interactive environment.

Start the tutorial

Python Projects for Beginners

If you’re learning Python, these projects are designed as step-by-step tutorials to help you learn fundamental concepts of Python programming. If you want a guided curriculum, our Python with Dr. Johns course takes a more academic approach.

Mad Libs Generator

Difficulty: Beginner | Estimated time: 30 to 60 minutes | Skills: strings, loops, user input

Terminal-based Python Mad Libs generator using user input and string formatting.

A Mad Libs generator is a fun way to practice user input, string manipulation, and loops. Users enter words that fill a story template for surprising results.

Start the tutorial

Number Guessing

Difficulty: Beginner | Estimated time: 30 to 60 minutes | Skills: random, loops, conditionals

Terminal-based Python number guessing game with hints and score tracking.

A number guessing game introduces random number generation, loops, and user input handling. It’s a fast build that still teaches real fundamentals.

Start the tutorial

Rock Paper Scissors

Difficulty: Beginner | Estimated time: 30 to 60 minutes | Skills: conditionals, loops, input validation

Terminal-based Python Rock Paper Scissors game against the computer.

A Rock Paper Scissors game reinforces user input handling, randomization, and game logic. Add scorekeeping and best-of rounds to make it portfolio-friendly.

Start the tutorial

Dice Roll Generator

Difficulty: Beginner | Estimated time: 30 to 60 minutes | Skills: random, loops, validation

Terminal-based Python dice roll generator for one or two dice.

A dice roll generator simulates rolling dice for board games and RPGs. It’s a simple project that teaches user input validation and loops.

Start the tutorial

Calculator

Difficulty: Beginner | Estimated time: 45 to 90 minutes | Skills: functions, loops, basic state

Terminal-based Python calculator with arithmetic and memory features.

A Python calculator is a classic project that teaches input handling, function-based modular programming, loops, and conditional logic.

Start the tutorial

Password Strength Checker

Difficulty: Beginner to intermediate | Estimated time: 60 to 120 minutes | Skills: security basics, entropy, validation

Terminal-based Python password strength checker using entropy and character rules.

A password strength checker evaluates passwords based on entropy, length, and character diversity. It’s a useful project for anyone interested in security fundamentals.

Start the tutorial

Countdown Clock and Timer

Difficulty: Beginner | Estimated time: 30 to 60 minutes | Skills: time formatting, loops, alerts

Terminal-based Python countdown timer with a realtime display.

A countdown timer tracks time with a clear, real-time display and an alert when the countdown reaches zero. It’s practical and quick to build.

Start the tutorial

Number to Words

Difficulty: Beginner | Estimated time: 60 to 120 minutes | Skills: conditionals, structured design

Terminal-based Python numbers-to-words converter.

A numbers-to-words converter transforms numeric values into written English equivalents. It’s a great way to practice structured function design and logic.

Start the tutorial

Fibonacci Sequence Generator

Difficulty: Beginner to intermediate | Estimated time: 60 to 120 minutes | Skills: recursion, memoization, performance

Terminal-based Python Fibonacci generator demonstrating recursion and optimization.

A Fibonacci generator helps you learn recursion, memoization, and iterative optimization. It’s also a great stepping stone into performance thinking.

Start the tutorial

Bonus Project: Age Calculator

Difficulty: Beginner | Estimated time: 30 to 60 minutes | Skills: datetime, parsing, validation

We built this after a reader request. The goal is simple: parse dates safely, calculate age correctly, and handle edge cases. If you want a quick win that still teaches real-world input handling, this is a strong choice.

Practical Python Tools and Utilities

Python Email Sender with Gmail and SMTP

Difficulty: Intermediate | Estimated time: 1 to 2 hours | Skills: SMTP, credential handling, automation

A Gmail SMTP email sender lets you send emails programmatically, which is useful for notifications, reports, and alerts. For 2026 best practices, store credentials in environment variables and use OAuth or app passwords where applicable.

Start the tutorial

Password Generator

Difficulty: Beginner to intermediate | Estimated time: 60 to 120 minutes | Skills: randomness, entropy, UX

Python secure password generator with strength feedback.

A password generator creates strong random passwords and can evaluate their strength. It’s a useful project for learning secure randomness and user-friendly output.

Start the tutorial

Site Connectivity Checker

Difficulty: Beginner to intermediate | Estimated time: 1 to 2 hours | Skills: requests, errors, GUI

Python site connectivity checker app with a PyQt GUI.

A connectivity checker verifies whether a site is online. It’s a practical way to learn network requests, error handling, and GUI workflows.

Start the tutorial

Network Speed Test App

Difficulty: Intermediate | Estimated time: 1 to 2 hours | Skills: measurement, real-time UI updates

Python network speed test app with a PyQt GUI.

A network speed test app measures download, upload, and ping. It’s a solid project to learn UI updates, network tooling, and clean output formatting.

Start the tutorial

Secure File Eraser

Difficulty: Intermediate | Estimated time: 1 to 3 hours | Skills: secure deletion concepts, GUI

Python secure file eraser app with a PyQt GUI.

A secure file eraser permanently deletes files beyond simple recovery. Use it responsibly, and always test on non-critical data first.

Start the tutorial

File Encryption Tool

Difficulty: Intermediate | Estimated time: 1 to 3 hours | Skills: cryptography fundamentals, UX

Python file encryption tool with a PyQt GUI.

A file encryption tool helps you learn encryption basics and safe handling of sensitive files. Treat key management as part of the project, not an afterthought.

Start the tutorial

Currency Converter

This project uses the requests library and a third-party API to convert currencies. This doesn’t ship with the Python standard library, so install it with pip.

For 2026 best practices, store API keys in environment variables rather than hard-coding them.

Source Code:

'''
Currency Converter
-------------------------------------------------------------
pip install requests

Recommended:
export FIXER_API_KEY="your_key_here"
'''

import os
import requests

def convert_currency():
    init_currency = input('Enter an initial currency: ').strip().upper()
    target_currency = input('Enter a target currency: ').strip().upper()

    while True:
        try:
            amount = float(input('Enter the amount: '))
        except ValueError:
            print('The amount must be a numeric value.')
            continue

        if amount <= 0:
            print('The amount must be greater than 0.')
            continue
        break

    url = (
        'https://api.apilayer.com/fixer/convert?to='
        + target_currency + '&from=' + init_currency +
        '&amount=' + str(amount)
    )

    api_key = os.getenv('FIXER_API_KEY', 'YOUR_API_KEY')
    headers = {'apikey': api_key}

    response = requests.get(url, headers=headers, timeout=30)
    if response.status_code != 200:
        print('There was a problem. Please try again later.')
        raise SystemExit(1)

    result = response.json()
    print('Conversion result: ' + str(result.get('result')))

if __name__ == '__main__':
    convert_currency()

Automatic Birthday Mail Sending

This project reads birthdays from an Excel file and sends an email on the correct date. Use environment variables for credentials and prefer OAuth or app passwords when available.

Source Code:

'''
Birthday Email Sender
-------------------------------------------------------------
pip install pandas openpyxl

Excel cols:
Name, Email, Birthday (MM/DD/YYYY), Last Sent (YYYY)

Recommended environment variables:
GMAIL_ID="your_email"
GMAIL_APP_PASSWORD="your_app_password"
'''

import os
import pandas as pd
from datetime import datetime
import smtplib
from email.message import EmailMessage

GMAIL_ID = os.getenv('GMAIL_ID', '')
GMAIL_APP_PASSWORD = os.getenv('GMAIL_APP_PASSWORD', '')

def send_email(recipient, subject, msg):
    if not GMAIL_ID or not GMAIL_APP_PASSWORD:
        raise RuntimeError('Missing email credentials. Set environment variables.')

    email = EmailMessage()
    email['Subject'] = subject
    email['From'] = GMAIL_ID
    email['To'] = recipient
    email.set_content(msg)

    with smtplib.SMTP_SSL('smtp.gmail.com', 465) as gmail_obj:
        gmail_obj.login(GMAIL_ID, GMAIL_APP_PASSWORD)
        gmail_obj.send_message(email)

def send_bday_emails(bday_file):
    bdays_df = pd.read_excel(bday_file)
    today = datetime.now().strftime('%m-%d')
    year_now = datetime.now().strftime('%Y')
    sent_index = []

    for idx, item in bdays_df.iterrows():
        bday = item['Birthday'].to_pydatetime().strftime('%m-%d')
        if (today == bday) and year_now not in str(item.get('Last Sent', '')):
            msg = 'Happy Birthday ' + str(item['Name']) + '!!'
            send_email(item['Email'], 'Happy Birthday', msg)
            sent_index.append(idx)

    for idx in sent_index:
        bdays_df.loc[bdays_df.index[idx], 'Last Sent'] = str(year_now)

    bdays_df.to_excel(bday_file, index=False)

if __name__ == '__main__':
    send_bday_emails(bday_file='your_bdays_list.xlsx')

Language Detector

This project uses langdetect to identify the language of user input and displays the result in a simple Tkinter GUI.

Source Code:

'''
Language Detector
-------------------------------------------------------------
pip install langdetect
'''

from langdetect import detect
import tkinter as tk

def detect_lang():
    window = tk.Tk()
    window.geometry('600x400')
    head = tk.Label(window, text='Language Detector', font=('Calibri 15'))
    head.pack(pady=20)

    def check_language():
        new_text = text.get()
        lang = detect(str(new_text))
        tk.Label(window, text=lang, font=('Calibri 15')).place(x=260, y=200)

    text = tk.StringVar()
    tk.Entry(window, textvariable=text).place(x=200, y=80, height=30, width=280)
    tk.Button(window, text='Check Language', command=check_language).place(x=285, y=150)
    window.mainloop()

if __name__ == '__main__':
    detect_lang()

Text to Speech

This project converts an article URL into an mp3 file using nltk, newspaper3k, and gtts.

Source Code:

'''
Text To Speech
-------------------------------------------------------------
pip install nltk newspaper3k gtts
'''

import nltk
from newspaper import Article
from gtts import gTTS

def text_to_speech(url):
    article = Article(url)
    article.download()
    article.parse()
    nltk.download('punkt')
    article.nlp()
    article_text = article.text
    language = 'en'
    my_obj = gTTS(text=article_text, lang=language, slow=False)
    my_obj.save("read_article.mp3")

if __name__ == '__main__':
    text_to_speech(
        url='https://hackr.io/blog/top-tech-companies-hiring-python-developers'
    )

Text Editor

This project creates a basic text editor GUI with open and save functionality.

Source Code:

'''
Text Editor
-------------------------------------------------------------
'''

import tkinter as tk
from tkinter.filedialog import askopenfilename, asksaveasfilename

def text_editor():
    def open_file():
        filepath = askopenfilename(
            filetypes=[('Text Files', '*.txt'), ('All Files', '*.*')]
        )
        if not filepath:
            return

        txt_edit.delete(1.0, tk.END)
        with open(filepath, 'r') as input_file:
            text = input_file.read()
            txt_edit.insert(tk.END, text)
        window.title(f'TextEditor - {filepath}')

    def save_file():
        filepath = asksaveasfilename(
            defaultextension='txt',
            filetypes=[('Text Files', '*.txt'), ('All Files', '*.*')],
        )
        if not filepath:
            return

        with open(filepath, 'w') as output_file:
            text = txt_edit.get(1.0, tk.END)
            output_file.write(text)
        window.title(f'Text Editor - {filepath}')

    window = tk.Tk()
    window.title('Text Editor')
    window.rowconfigure(0, minsize=800, weight=1)
    window.columnconfigure(1, minsize=800, weight=1)

    txt_edit = tk.Text(window)
    fr_buttons = tk.Frame(window, relief=tk.RAISED, bd=2)
    btn_open = tk.Button(fr_buttons, text='Open', command=open_file)
    btn_save = tk.Button(fr_buttons, text='Save As...', command=save_file)

    btn_open.grid(row=0, column=0, sticky='ew', padx=5, pady=5)
    btn_save.grid(row=1, column=0, sticky='ew', padx=5)

    fr_buttons.grid(row=0, column=0, sticky='ns')
    txt_edit.grid(row=0, column=1, sticky='nsew')

    window.mainloop()

if __name__ == '__main__':
    text_editor()

Python Data Structures and Algorithms

Binary Search Algorithm

Binary search is a rite of passage for many Python learners. It’s also a great way to build confidence with time complexity and problem decomposition.

Source Code:

'''
Binary Search
-------------------------------------------------------------
'''

def binary_search(a_list, an_item):
    first = 0
    last = len(a_list) - 1

    while first <= last:
        mid_point = (first + last) // 2
        if a_list[mid_point] == an_item:
            return True
        if an_item < a_list[mid_point]:
            last = mid_point - 1
        else:
            first = mid_point + 1
    return False

def binary_search_rec(a_list, first, last, an_item):
    if len(a_list) == 0:
        return False

    mid_point = (first + last) // 2
    if a_list[mid_point] == an_item:
        return True

    if an_item < a_list[mid_point]:
        return binary_search_rec(a_list, first, mid_point - 1, an_item)
    return binary_search_rec(a_list, mid_point + 1, last, an_item)

if __name__ == '__main__':
    a_list = [1, 4, 7, 10, 14, 19, 102, 2575, 10000]
    print('Binary Search:', binary_search(a_list, 4))
    print('Binary Search Recursive:', binary_search_rec(a_list, 0, len(a_list) - 1, 4))

Merge Sort Algorithm

Merge sort is a classic divide-and-conquer algorithm that demonstrates recursion and the idea of combining sorted results.

Source Code:

'''
Merge Sort
-------------------------------------------------------------
'''

def merge_sort(a_list):
    if len(a_list) <= 1:
        return a_list

    mid_point = len(a_list) // 2
    left_half = merge_sort(a_list[:mid_point])
    right_half = merge_sort(a_list[mid_point:])

    merged = []
    i = 0
    j = 0

    while i < len(left_half) and j < len(right_half):
        if left_half[i] <= right_half[j]:
            merged.append(left_half[i])
            i += 1
        else:
            merged.append(right_half[j])
            j += 1

    merged.extend(left_half[i:])
    merged.extend(right_half[j:])
    return merged

if __name__ == '__main__':
    a_list = [45, 7, 85, 24, 60, 25, 38, 63, 1]
    print(merge_sort(a_list))

Queue

A queue is a common data structure when you need First-In-First-Out behavior, like message queues or task pipelines.

Source Code:

'''
Queue Data Structure
-------------------------------------------------------------
'''

class Queue:
    def __init__(self):
        self.items = []

    def __repr__(self):
        return f'Queue object: data={self.items}'

    def is_empty(self):
        return not self.items

    def enqueue(self, item):
        self.items.append(item)

    def dequeue(self):
        return self.items.pop(0)

    def size(self):
        return len(self.items)

    def peek(self):
        return self.items[0]

if __name__ == '__main__':
    q = Queue()
    print(q.is_empty())
    q.enqueue('First')
    q.enqueue('Second')
    print(q)
    print(q.dequeue())
    print(q)
    print(q.size())
    print(q.peek())

Sudoku Solver

This project builds a GUI sudoku solver using backtracking. It’s a great way to learn recursion, state, and UI updates.

Source Code:

'''
Sudoku Solver
-------------------------------------------------------------
pip install pygame
image link:
https://www.pngitem.com/pimgs/m/210-2106648_empty-sudoku-grid-grid-6x6-png-transparent-png.png
'''

import pygame

pygame.font.init()
screen = pygame.display.set_mode((600, 600))
pygame.display.set_caption('SUDOKU SOLVER USING BACKTRACKING')
img = pygame.image.load('icon.png')
pygame.display.set_icon(img)
font1 = pygame.font.SysFont('comicsans', 40)
font2 = pygame.font.SysFont('comicsans', 20)
x = 0
y = 0
dif = 500 / 9
val = 0

grid = [
    [7, 8, 0, 4, 0, 0, 1, 2, 0],
    [6, 0, 0, 0, 7, 5, 0, 0, 9],
    [0, 0, 0, 6, 0, 1, 0, 7, 8],
    [0, 0, 7, 0, 4, 0, 2, 6, 0],
    [0, 0, 1, 0, 5, 0, 9, 3, 0],
    [9, 0, 4, 0, 6, 0, 0, 0, 5],
    [0, 7, 0, 3, 0, 0, 0, 1, 2],
    [1, 2, 0, 0, 0, 7, 4, 0, 0],
    [0, 4, 9, 2, 0, 6, 0, 0, 7]
]

def get_coord(pos):
    global x, y
    x = pos[0] // dif
    y = pos[1] // dif

def draw_box():
    for i in range(2):
        pygame.draw.line(screen, (255, 0, 0), (x * dif - 3, (y + i) * dif), (x * dif + dif + 3, (y + i) * dif), 7)
        pygame.draw.line(screen, (255, 0, 0), ((x + i) * dif, y * dif), ((x + i) * dif, y * dif + dif), 7)

def draw():
    for i in range(9):
        for j in range(9):
            if grid[i][j] != 0:
                pygame.draw.rect(screen, (0, 153, 153), (i * dif, j * dif, dif + 1, dif + 1))
                text1 = font1.render(str(grid[i][j]), 1, (0, 0, 0))
                screen.blit(text1, (i * dif + 15, j * dif + 15))

    for i in range(10):
        thick = 7 if i % 3 == 0 else 1
        pygame.draw.line(screen, (0, 0, 0), (0, i * dif), (500, i * dif), thick)
        pygame.draw.line(screen, (0, 0, 0), (i * dif, 0), (i * dif, 500), thick)

def draw_val(val):
    text1 = font1.render(str(val), 1, (0, 0, 0))
    screen.blit(text1, (x * dif + 15, y * dif + 15))

def raise_error_1():
    text1 = font1.render('WRONG !!!', 1, (0, 0, 0))
    screen.blit(text1, (20, 570))

def raise_error_2():
    text1 = font1.render('Wrong !!! Not a valid Key', 1, (0, 0, 0))
    screen.blit(text1, (20, 570))

def valid(m, i, j, val):
    for it in range(9):
        if m[i][it] == val:
            return False
        if m[it][j] == val:
            return False

    it = i // 3
    jt = j // 3

    for a in range(it * 3, it * 3 + 3):
        for b in range(jt * 3, jt * 3 + 3):
            if m[a][b] == val:
                return False
    return True

def solve(grid, i, j):
    while grid[i][j] != 0:
        if i < 8:
            i += 1
        elif i == 8 and j < 8:
            i = 0
            j += 1
        else:
            return True

    pygame.event.pump()
    for it in range(1, 10):
        if valid(grid, i, j, it):
            grid[i][j] = it
            screen.fill((255, 255, 255))
            draw()
            draw_box()
            pygame.display.update()
            pygame.time.delay(20)

            if solve(grid, i, j):
                return True

            grid[i][j] = 0
            screen.fill((255, 255, 255))
            draw()
            draw_box()
            pygame.display.update()
            pygame.time.delay(50)
    return False

def instruction():
    text1 = font2.render('PRESS D TO RESET TO DEFAULT / R TO EMPTY', 1, (0, 0, 0))
    text2 = font2.render('ENTER VALUES AND PRESS ENTER TO VISUALIZE', 1, (0, 0, 0))
    screen.blit(text1, (20, 520))
    screen.blit(text2, (20, 540))

def result():
    text1 = font1.render('FINISHED PRESS R or D', 1, (0, 0, 0))
    screen.blit(text1, (20, 570))

run = True
flag_1 = 0
flag_2 = 0
rs = 0
error = 0
val = 0

while run:
    screen.fill((255, 255, 255))
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

        if event.type == pygame.MOUSEBUTTONDOWN:
            flag_1 = 1
            pos = pygame.mouse.get_pos()
            get_coord(pos)

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                x -= 1
                flag_1 = 1
            if event.key == pygame.K_RIGHT:
                x += 1
                flag_1 = 1
            if event.key == pygame.K_UP:
                y -= 1
                flag_1 = 1
            if event.key == pygame.K_DOWN:
                y += 1
                flag_1 = 1

            if event.key in (pygame.K_1, pygame.K_2, pygame.K_3, pygame.K_4, pygame.K_5, pygame.K_6, pygame.K_7, pygame.K_8, pygame.K_9):
                val = int(event.unicode)

            if event.key == pygame.K_RETURN:
                flag_2 = 1

            if event.key == pygame.K_r:
                rs = 0
                error = 0
                flag_2 = 0
                grid = [[0] * 9 for _ in range(9)]

            if event.key == pygame.K_d:
                rs = 0
                error = 0
                flag_2 = 0
                grid = [
                    [7, 8, 0, 4, 0, 0, 1, 2, 0],
                    [6, 0, 0, 0, 7, 5, 0, 0, 9],
                    [0, 0, 0, 6, 0, 1, 0, 7, 8],
                    [0, 0, 7, 0, 4, 0, 2, 6, 0],
                    [0, 0, 1, 0, 5, 0, 9, 3, 0],
                    [9, 0, 4, 0, 6, 0, 0, 0, 5],
                    [0, 7, 0, 3, 0, 0, 0, 1, 2],
                    [1, 2, 0, 0, 0, 7, 4, 0, 0],
                    [0, 4, 9, 2, 0, 6, 0, 0, 7]
                ]

    if flag_2 == 1:
        if not solve(grid, 0, 0):
            error = 1
        else:
            rs = 1
        flag_2 = 0

    if val != 0:
        draw_val(val)
        if valid(grid, int(x), int(y), val):
            grid[int(x)][int(y)] = val
            flag_1 = 0
        else:
            grid[int(x)][int(y)] = 0
            raise_error_2()
        val = 0

    if error == 1:
        raise_error_1()
    if rs == 1:
        result()

    draw()
    if flag_1 == 1:
        draw_box()
    instruction()
    pygame.display.update()

Python Bot Projects

Reddit Bot

This project creates an automated Reddit bot using praw and pyenchant. Use it responsibly, respect subreddit rules, and add rate limiting to avoid spammy behavior.

Important note: You’ll need to follow these instructions to get a client_id, client_secret, username, password, and user_agent.

Source Code:

'''
Reddit Reply Bot
-------------------------------------------------------------
pip install praw pyenchant
'''

import praw
import enchant

def reddit_bot(sub, trigger_phrase):
    reddit = praw.Reddit(
        client_id='your_client_id',
        client_secret='your_client_secret',
        username='your_username',
        password='your_pw',
        user_agent='your_user_agent'
    )

    subreddit = reddit.subreddit(sub)
    dict_suggest = enchant.Dict('en_US')

    for comment in subreddit.stream.comments():
        if trigger_phrase in comment.body.lower():
            word = comment.body.replace(trigger_phrase, '')
            reply_text = ''
            similar_words = dict_suggest.suggest(word)
            for similar in similar_words:
                reply_text += (similar + ' ')
            comment.reply(reply_text)

if __name__ == '__main__':
    reddit_bot(sub='Python', trigger_phrase='useful bot')

Chatbot (Legacy Project)

This chatbot example uses the chatterbot library. It has seen limited maintenance for years, so treat it as a learning exercise rather than a modern production pattern.

If you want a more current chatbot build, we recommend a rules-and-intents chatbot, or an LLM-based chatbot with rate limiting and safety checks.

Source Code:

'''
Chat Bot (Legacy)
-------------------------------------------------------------
1) pip install ChatterBot chatterbot-corpus spacy
2) python3 -m spacy download en_core_web_sm
'''

from chatterbot import ChatBot
from chatterbot.trainers import ChatterBotCorpusTrainer

def create_chat_bot():
    chatbot = ChatBot('Chattering Bot')
    trainer = ChatterBotCorpusTrainer(chatbot)
    trainer.train('chatterbot.corpus.english')

    while True:
        try:
            bot_input = chatbot.get_response(input())
            print(bot_input)
        except (KeyboardInterrupt, EOFError, SystemExit):
            break

if __name__ == '__main__':
    create_chat_bot()

Advanced Python Projects

Library Management System

This project uses object-oriented programming to simulate a library management system. It’s a great base for expanding into databases, unique IDs, due dates, and simple GUIs.

Source Code:

'''
Library
-------------------------------------------------------------
'''

class Library:
    def __init__(self, books):
        self.books = books

    def show_avail_books(self):
        print('Our Library Can Offer You The Following Books:')
        print('================================================')
        for book, borrower in self.books.items():
            if borrower == 'Free':
                print(book)

    def lend_book(self, requested_book, name):
        if self.books[requested_book] == 'Free':
            print(f'{requested_book} has been marked as Borrowed by: {name}')
            self.books[requested_book] = name
            return True
        print(f'Sorry, the {requested_book} is currently on loan to: {self.books[requested_book]}')
        return False

    def return_book(self, returned_book):
        self.books[returned_book] = 'Free'
        print(f'Thanks for returning {returned_book}')

class Student:
    def __init__(self, name, library):
        self.name = name
        self.books = []
        self.library = library

    def view_borrowed(self):
        if not self.books:
            print('You have not borrowed any books')
        else:
            for book in self.books:
                print(book)

    def request_book(self):
        book = input('Enter the name of the book you would like to borrow >> ')
        if self.library.lend_book(book, self.name):
            self.books.append(book)

    def return_book(self):
        book = input('Enter the name of the book you would like to return >> ')
        if book in self.books:
            self.library.return_book(book)
        else:
            print('You have not borrowed that book, try another.')

def create_lib():
    books = {
        'The Last Battle': 'Free',
        'The Hunger Games': 'Free',
        'Cracking the Coding Interview': 'Free'
    }
    library = Library(books)
    student_example = Student('Your Name', library)

    while True:
        print('''
==========LIBRARY MENU===========
1. Display Available Books
2. Borrow a Book
3. Return a Book
4. View Your Books
5. Exit
''')
        choice = int(input('Enter Choice: '))
        if choice == 1:
            library.show_avail_books()
        elif choice == 2:
            student_example.request_book()
        elif choice == 3:
            student_example.return_book()
        elif choice == 4:
            student_example.view_borrowed()
        elif choice == 5:
            print('Goodbye')
            raise SystemExit(0)

if __name__ == '__main__':
    create_lib()

Netflix Recommendation System

This project builds a simple recommendation engine using a Netflix dataset. The example below fixes a common column-name mismatch and keeps the overall approach intact. For a stronger 2026 portfolio version, swap in a newer dataset and add evaluation, caching, and a clean UI layer.

Source Code:

'''
Netflix Recommendation Engine
-------------------------------------------------------------
pip install pandas numpy nltk
'''

from nltk.tokenize import word_tokenize
import numpy as np
import pandas as pd
import re
import nltk
import tkinter as tk
from nltk.corpus import stopwords

nltk.download('stopwords')

data = pd.read_csv('netflixData.csv')
data = data.dropna(subset=['Cast', 'Production Country', 'Rating'])

movies = data[data['Content Type'] == 'Movie'].reset_index()
movies = movies.drop(['index', 'Show Id', 'Content Type', 'Date Added',
                      'Release Date', 'Duration', 'Description'], axis=1)

tv = data[data['Content Type'] == 'TV Show'].reset_index()
tv = tv.drop(['index', 'Show Id', 'Content Type', 'Date Added',
              'Release Date', 'Duration', 'Description'], axis=1)

# Note: feature engineering code omitted here for brevity, keep your existing blocks above.
# Ensure the final feature matrices are named `binary` for movies and `binary_2` for TV.

window = tk.Tk()
window.geometry('600x600')
head = tk.Label(window, text='Enter a Netflix Movie or TV Show for Recommendations', font=('Calibri 15'))
head.pack(pady=20)

def netflix_recommender(search):
    cs_list = []
    binary_list = []

    if search in movies['Title'].values:
        idx = movies[movies['Title'] == search].index.item()
        for i in binary.iloc[idx]:
            binary_list.append(i)

        point_1 = np.array(binary_list).reshape(1, -1)
        point_1 = [val for sublist in point_1 for val in sublist]

        for j in range(len(movies)):
            binary_list_2 = []
            for k in binary.iloc[j]:
                binary_list_2.append(k)
            point_2 = np.array(binary_list_2).reshape(1, -1)
            point_2 = [val for sublist in point_2 for val in sublist]

            dot_product = np.dot(point_1, point_2)
            norm_1 = np.linalg.norm(point_1)
            norm_2 = np.linalg.norm(point_2)
            cos_sim = dot_product / (norm_1 * norm_2)
            cs_list.append(cos_sim)

        movies_copy = movies.copy()
        movies_copy['cos_sim'] = cs_list
        results = movies_copy.sort_values('cos_sim', ascending=False)

        results = results[results['Title'] != search]
        return results.head(5)

    if search in tv['Title'].values:
        idx = tv[tv['Title'] == search].index.item()
        for i in binary_2.iloc[idx]:
            binary_list.append(i)

        point_1 = np.array(binary_list).reshape(1, -1)
        point_1 = [val for sublist in point_1 for val in sublist]

        for j in range(len(tv)):
            binary_list_2 = []
            for k in binary_2.iloc[j]:
                binary_list_2.append(k)
            point_2 = np.array(binary_list_2).reshape(1, -1)
            point_2 = [val for sublist in point_2 for val in sublist]

            dot_product = np.dot(point_1, point_2)
            norm_1 = np.linalg.norm(point_1)
            norm_2 = np.linalg.norm(point_2)
            cos_sim = dot_product / (norm_1 * norm_2)
            cs_list.append(cos_sim)

        tv_copy = tv.copy()
        tv_copy['cos_sim'] = cs_list
        results = tv_copy.sort_values('cos_sim', ascending=False)
        results = results[results['Title'] != search]
        return results.head(5)

    return 'Title not in dataset. Please check spelling.'

def call_recommender():
    subject = text.get()
    recommendation = netflix_recommender(subject)
    txt = ''
    if isinstance(recommendation, str):
        txt = recommendation
    else:
        for _, row in recommendation.iterrows():
            txt += 'Title: ' + str(row['Title']) + '\n'
    tk.Label(window, text=txt, font=('Calibri 12'), justify='left').place(x=60, y=180)

text = tk.StringVar()
tk.Entry(window, textvariable=text).place(x=60, y=80, height=30, width=480)
tk.Button(window, text='Find Recommendations', command=call_recommender).place(x=240, y=130)
window.mainloop()

Where To Start With Python

Today, we’re seeing growing adoption of AI, machine learning, and data science across most business sectors. These fields often rely on Python.

When it comes to learning Python in 2026, you have many options. If you prefer an in-depth and interactive learning experience, we also recommend our Python course.

What Should I Build Using Python?

If you’re new to coding in Python, start with something small, then scale. Python supports web development, data work, automation, and tooling, so you can choose projects that match your goals.

That’s why we put this list together, so you can pick a project and start building.

Wrapping Up

Building projects is the fastest way to learn, and now you have a list of real-world, portfolio-worthy ideas to work on.

Pick a project, start coding, and watch your Python skills grow. We keep adding new projects, so bookmark this page and check back for more.

Happy coding.

Enjoyed tackling these Python projects and want to go deeper? Check out:

Our Python Masterclass, Python with Dr. Johns

References

1. Bureau of Labor Statistics, U.S. Department of Labor. Occupational Outlook Handbook, Software Developers [Internet]. Accessed 2026-02-19.

2. apilayer. Fixer API pricing [Internet]. Accessed 2026-02-19.

3. Reddit archive. OAuth2 Quick Start Example [Internet]. Accessed 2026-02-19.

4. Makhija, S. Netflix Movies and TV Shows 2021 [dataset]. Kaggle; 2021. Accessed 2026-02-19.

By Robert Johns

Technical Editor for Hackr.io | 15+ Years in Python, Java, SQL, C++, C#, JavaScript, Ruby, PHP, .NET, MATLAB, HTML & CSS, and more... 10+ Years in Networking, Cloud, APIs, Linux | 5+ Years in Data Science | 2x PhDs in Structural & Blast Engineering

View all post by the author

Subscribe to our Newsletter for Articles, News, & Jobs.

I accept the Terms and Conditions.

Disclosure: Hackr.io is supported by its audience. When you purchase through links on our site, we may earn an affiliate commission.

Featured Resources

Learn More

Please login to leave comments

Image

Akib Dewan

all codes has so many mistakes

5 years ago

Image

Robert Johns

Thanks for pointing this out. If you take a look at our latest projects, they should be all bug-free.

2 years ago

Image

Robert Johns

Thanks for pointing this out. If you take a look at our latest projects, they should be all bug-free.

2 years ago

Image

Esther Ndosi

Thank you for all these cool projects. Its great to start a year with them. On the second project I think the attempts should be appended after the guess == rand_num as it always print 1 and i think it should be the lowest attempts the user has. thank you.

2 years ago

Image

Robert Johns

Thanks for taking the time to build these projects and spotting that. The second project has been updated, check it out and see what you think.

2 years ago

Image

Robert Johns

Thanks for taking the time to build these projects and spotting that. The second project has been updated, check it out and see what you think.

2 years ago

Image

Akash Roy

Embarking on a Python journey with these hands-on projects feels like unlocking a treasure trove of coding adventures!

1 year ago