Skip to content

Instantly share code, notes, and snippets.

@vicradon
Last active August 27, 2025 21:44
Show Gist options
  • Save vicradon/6398fa9c5b0f0ad284a9fb7a22bc98a1 to your computer and use it in GitHub Desktop.
Save vicradon/6398fa9c5b0f0ad284a9fb7a22bc98a1 to your computer and use it in GitHub Desktop.
intermediate python

Intermediate Python

Intermediate concepts in Python progrmming like:

  1. Working with files
  2. The standard library
  3. Virtual Environments
  4. Working with modules and packges
  5. Making API requests

Section 1: Working with Files and Context

Programs can create data or require data as input during their running time. A very common way to supply data to a running program is via a file. A program may store its intermediate results to a file before post processing and final storage to a storage bucket (S3) or database.

Basic File Operations

There are two basic things to do with files

  1. Reading from a file
  2. Writing to a file
# Write to a text file
file = open("people-who-owe-me.txt", "w")
file.write("Emmanuel\n")
file.write("Chidera\n")
file.close()
# Read from a text file
file = open("people-who-owe-me.txt", "r")
file.seek(0)
content = file.read()
print("People that owe me money:")
print(content)
file.close()
# Append to file a text file
filename = "people-who-owe-me.txt"
mode = "a"
# first write operation
file = open(filename, mode)
file.write("Bisola\n")
file.close()
# second write operation
file = open(filename, mode)
file.write("Ganiu\n")
file.close()
# read what has been written so far
file = open(filename, "r")
file.seek(0)
content = file.read()
print("People that owe me money:")
print(content)
file.close()

Reading and Writing Explanation

Reading and writing files follows this format:

open
   |
   -> write
          |
          -> close

It's important you close the file after your operations so your program does not cause resource exhaustion.

with open("people-who-owe-me.md") as file:
content = file.read()
print(content)
file.write("Ibrahim")
file.write("Mustapha")
print(file.read())
# no need for close

Why this format

The with operator is called a context manager. If implemented on a function, like the open function, allows you use resources in a context without worrying about closing/deleting them.

import sqlite3
import datetime
connection = sqlite3.connect("people_who_owe_me.db")
cursor = connection.cursor()
cursor.execute(
"""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
address TEXT NOT NULL,
deadline TIMESTAMP
)
"""
)
cursor.execute(
"""
INSERT INTO users (name, address, deadline)
VALUES (?, ?, ?)
""",
(
"Emmanuel",
"No. 5 Orile, Lagos",
datetime.datetime.now() + datetime.timedelta(hours=24)
)
)
# connection.commit()
# connection.close()
import sqlite3
import datetime
with sqlite3.connect("people_who_owe_me.db") as connection:
cursor = connection.cursor()
cursor.execute(
"""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
address TEXT NOT NULL,
deadline TIMESTAMP
)
"""
)
cursor.execute("""
INSERT INTO users (name, address, deadline)
VALUES (?, ?, ?)
""",
(
"Emmanuel",
"No. 5 Orile, Lagos",
datetime.datetime.now() + datetime.timedelta(hours=24)
)
)

Class Exercise on working with files

Task: Modify your input collection program from yesterday to write its output to a file

Starting point:

name = input("What is your name: ")
age = int(input("What is your age: "))

interests = ""

for x in range(3):
    interest_x = input(f"What is your number {x+1} interest: ")

    # if we are at the beginging
    if x == 0:
        interests += f"{interest_x}"

    # if we are the end
    elif x == 2: 
        interests += f", and {interest_x}"

    # every other number should have commas in-between
    else:
        interests += f", {interest_x}"


combined = f"Your name is {name} and you are {age} years old and your interests are {interests}"

print(combined)

# TODO: Write this combined to a file in append mode
# This ensures that new user content gets written at the end
# don't forget to add a new line
name = input("What is your name: ")
age = int(input("What is your age: "))
interests = ""
for x in range(3):
interest_x = input(f"What is your number {x+1} interest: ")
# if we are at the beginging
if x == 0:
interests += f"{interest_x}"
# if we are the end
elif x == 2:
interests += f", and {interest_x}"
# every other number should have commas in-between
else:
interests += f", {interest_x}"
combined = f"Your name is {name} and you are {age} years old and your interests are {interests}"
print(combined)
# TODO: Write this combined to a file in append mode
# This ensures that new user content gets written at the end
# don't forget to add a new line

Section 2: Python Standard Library

The Python Standard Library allows us to make use of plenty goodies without relying on third parties.

You've already seen:

  1. Sqlite3,
  2. math, and
  3. datetime

But it goes beyond to include utilities for making API requests, working with folders, calling native OS utilities (for example, utility for opening an application, like Chrome).

This section shows in greater depth these modules:

  • datetime
  • random
  • math
  • tkinter
  • turtle
  • this, antigravity,
"""
Library path: https://docs.python.org/3/library/datetime.html
Date object: https://docs.python.org/3/library/datetime.html#date-objects
Datetime object: https://docs.python.org/3/library/datetime.html#datetime-objects
"""
import datetime
print("Today is:", datetime.date.today())
print("The time is: ", datetime.datetime.now().time())
import os
import time
import datetime
while True:
os.system("cls" if os.name == "nt" else "clear")
print("The time is: ", datetime.datetime.now().time().replace(microsecond=0))
time.sleep(1)
"""
Library docs: https://docs.python.org/3/library/random.html
.random() method: https://docs.python.org/3/library/random.html#random.random
.randint() method: https://docs.python.org/3/library/random.html#random.randint
.randrange() method: https://docs.python.org/3/library/random.html#random.randrange
"""
import random
print("A random number between 0 and 1 is", random.random())
import random
print("Row a dice:", random.randint(1, 6))
import random
choice = None
while True:
choice = input("Enter a choice of head or tail: ")
if choice.lower() != "head" and choice.lower() != "tail":
print("Please enter either head or tail")
else:
break
random_val_between_0_and_1 = random.random()
if random_val_between_0_and_1 > 0.5:
print("Head wins the toss")
else:
print("Tail wins the toss")
"""
Library docs: https://docs.python.org/3/library/math.html
Sqrt method: https://docs.python.org/3/library/math.html#math.sqrt
"""
import math
print("Square root of 144 is", math.sqrt(144))
print("Cube root of 27 is", math.cbrt(27))
print("Cosine of 0 is", math.cos(0))
print("Sine of 90 (π/2) is", math.sin(0.5 * math.pi))
import tkinter as tk
root = tk.Tk()
root.mainloop()
import tkinter as tk
# Create the main window
root = tk.Tk()
root.geometry("400x300")
root.title("Hello Tkinter")
# Create a simple label
label = tk.Label(root, text="Hey guys, I am Tkinter!") # font=("Helvetica", 20, "bold")
label.pack(padx=20, pady=20)
# Run the application
root.mainloop()
import tkinter as tk
def on_click():
label.config(text="User info verified!", font=("Helvetica", 16, "bold"))
root = tk.Tk()
root.geometry("400x300")
root.title("Button Example")
label = tk.Label(root, text="Verify user info", font=("Helvetica", 16, "normal"))
label.pack(padx=20, pady=10)
button = tk.Button(root, text="Verify", command=on_click, font=("Helvetica", 20, "bold"), fg="green")
button.pack(padx=20, pady=10)
root.mainloop()
import tkinter as tk
def show_input():
user_text = entry.get()
label.config(text=f"You typed: {user_text}")
root = tk.Tk()
root.geometry("400x300")
root.title("Input Example")
large_font = ("Helvetica", 16)
entry = tk.Entry(root, font=large_font)
entry.pack(padx=20, pady=10)
button = tk.Button(root, text="Submit", command=show_input, font=large_font)
button.pack(padx=20, pady=10)
label = tk.Label(root, text="Type something above", font=large_font)
label.pack(padx=20, pady=10)
root.mainloop()
import tkinter as tk
def calculate():
try:
expression = entry.get()
result = eval(expression)
label.config(text=f"Result: {result}")
except Exception:
label.config(text="Error in expression")
root = tk.Tk()
root.title("Basic Calculator")
large_font = ("Helvetica", 16)
entry = tk.Entry(root, width=30, font=large_font)
entry.pack(padx=20, pady=10)
button = tk.Button(root, text="Calculate", command=calculate, font=large_font)
button.pack(padx=20, pady=10)
label = tk.Label(root, text="Enter an expression (e.g., 2+3*4)", font=large_font)
label.pack(padx=20, pady=10)
root.mainloop()
import turtle
turtle.forward(100)
turtle.mainloop()
import turtle as t
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.exitonclick()
import turtle as t
t.width(10)
for i in range(4):
t.forward(100)
t.left(90)
t.exitonclick()
import turtle as t
t.width(10)
t.shape("turtle")
t.color("black") # pen color
t.fillcolor("green") # turtle shape fill color
t.penup()
t.goto(0, -200)
t.pendown()
t.circle(200)
t.exitonclick()
import antigravity

Class Exercise on Standard Library

Build a random guessing game where users have to predict the year of birth of famous people

# don't forget to import random
# List of famous people with their birth years
famous_people = [
("Michael Jackson", 1958),
("Bola Tinubu", 1952),
("Nelson Mandela", 1918),
("Adolf Hitler", 1889)
]
print("Welcome to the Famous People Birth Year Guessing Game!\n")
# Pick a random person
# Hint use random.choice(famous_people)
# person, birth_year =
# Ask the user to guess the birth year
# guess = int(input(f"When was {person} born? "))
# Check the guess and let the user know if they are correct or wrong

Section 3: Virtual Environments

Before python -m venv .venv, let's show that -m runs a module.

Running modules

Python allows you to run code that is part of the current environment, or the standard library if no environment is selected.

For example, you can run Python's http server to serve your local file content:

Run this in your command line:

python -m http.server

Then run this:

python -m venv .venv

Creating and Activating virtual env

python -m venv .venv
. .venv/Scripts/activate # windows (powershell)
source .venv/Scripts/activate # windows (CMD)

source .venv/bin/activate # Mac/Linux

What is inside .venv

.venv is a folder you created when you ran python -m venv .venv. It could be anything really.

It could be python -m venv env, or python -m venv .virtual_environment. The choice of name and choice of . at the start depends on your team. But it's best to stick to .venv.

Inside this .venv, there are several folders.

On Windows

$ ls -la .venv
total 8
drwxr-xr-x 5 USER 197121   0 Aug 26 14:00 ./
drwxr-xr-x 3 USER 197121   0 Aug 26 14:00 ../
-rw-r--r-- 1 USER 197121  71 Aug 26 14:00 .gitignore
drwxr-xr-x 2 USER 197121   0 Aug 26 14:00 Include/
drwxr-xr-x 3 USER 197121   0 Aug 26 14:00 Lib/
-rw-r--r-- 1 USER 197121 345 Aug 26 14:00 pyvenv.cfg
drwxr-xr-x 2 USER 197121   0 Aug 26 14:01 Scripts/

On Unix

$ ls -la .venv
drwxr-xr-x 5 osi osi 4096 Aug 26 13:51 .
drwxr-xr-x 3 osi osi 4096 Aug 26 13:51 ..
drwxr-xr-x 2 osi osi 4096 Aug 26 13:51 bin
drwxr-xr-x 3 osi osi 4096 Aug 26 13:51 include
drwxr-xr-x 3 osi osi 4096 Aug 26 13:51 lib
lrwxrwxrwx 1 osi osi    3 Aug 26 13:51 lib64 -> lib
-rw-r--r-- 1 osi osi  179 Aug 26 13:51 pyvenv.cfg

The most important is the Scripts directory (on Windows). Or the bin directory on Unix (Linux/MacOS).

They contain similar content:

Windows

drwxr-xr-x 2 osi osi   4096 Aug 26 14:01 .
drwxr-xr-x 5 osi osi   4096 Aug 26 14:00 ..
-rw-r--r-- 1 osi osi   2246 Aug 26 14:01 activate
-rw-r--r-- 1 osi osi   1041 Aug 26 14:01 activate.bat
-rw-r--r-- 1 osi osi   2300 Aug 26 14:01 activate.fish
-rw-r--r-- 1 osi osi  29221 Dec  3  2024 Activate.ps1
-rw-r--r-- 1 osi osi    393 Dec  3  2024 deactivate.bat
-rw-r--r-- 1 osi osi 108439 Aug 26 14:01 pip3.13.exe
-rw-r--r-- 1 osi osi 108439 Aug 26 14:01 pip3.exe
-rw-r--r-- 1 osi osi 108439 Aug 26 14:01 pip.exe
-rw-r--r-- 1 osi osi 254832 Dec  3  2024 python.exe
-rw-r--r-- 1 osi osi 250360 Dec  3  2024 pythonw.exe

Unix

drwxr-xr-x 2 osi osi 4096 Aug 26 13:51 .
drwxr-xr-x 5 osi osi 4096 Aug 26 13:51 ..
-rw-r--r-- 1 osi osi 2011 Aug 26 13:51 activate
-rw-r--r-- 1 osi osi  937 Aug 26 13:51 activate.csh
-rw-r--r-- 1 osi osi 2213 Aug 26 13:51 activate.fish
-rw-r--r-- 1 osi osi 9033 Aug 26 13:51 Activate.ps1
-rwxr-xr-x 1 osi osi  269 Aug 26 13:51 normalizer
-rwxr-xr-x 1 osi osi  257 Aug 26 13:51 pip
-rwxr-xr-x 1 osi osi  257 Aug 26 13:51 pip3
-rwxr-xr-x 1 osi osi  257 Aug 26 13:51 pip3.11
lrwxrwxrwx 1 osi osi    7 Aug 26 13:51 python -> python3
lrwxrwxrwx 1 osi osi   16 Aug 26 13:51 python3 -> /usr/bin/python3
lrwxrwxrwx 1 osi osi    7 Aug 26 13:51 python3.11 -> python3

Installing modules in a virtual environment

After creating and sourcing a virtual environment, the next best thing is to install dependencies and manage them

So to install the popular requests module, you run:

pip install requests

Then use it in a python file to make an API request

import requests

response = requests.get("https://jsonplaceholder.typicode.com/users/1")
print(response.text)

# Expected response
"""
{
  "id": 1,
  "name": "Leanne Graham",
  "username": "Bret",
  "email": "[email protected]",
  "address": {
    "street": "Kulas Light",
    "suite": "Apt. 556",
    "city": "Gwenborough",
    "zipcode": "92998-3874",
    "geo": {
      "lat": "-37.3159",
      "lng": "81.1496"
    }
  },
  "phone": "1-770-736-8031 x56442",
  "website": "hildegard.org",
  "company": {
    "name": "Romaguera-Crona",
    "catchPhrase": "Multi-layered client-server neural-net",
    "bs": "harness real-time e-markets"
  }
}
"""

After installing depdencies

You can see the content of your virtual environment by running pip freeze. If you installed requests in a new virtual environment, it comes with 4 other modules (certifi, charset-normalizer, idna, urllib3).

(.venv) osi@system:~/codes$ pip freeze
certifi==2025.8.3
charset-normalizer==3.4.3
idna==3.10
requests==2.32.5
urllib3==2.5.0

You should save this output to a file called requirements.txt (it's conventional). This file can then be used by other collaborators to update their environments.

To save it to a file, you can use the unix redirection operator (>):

(.venv) osi@system:~/codes$ pip freeze > requirements.txt

Opening up the requirements.txt file will show the same dependencies.

A collaborator can then run:

pip install -r requirements.txt

to install the depdencies in that env.

Class Exercise on virtual environments

  1. Create a new folder and open it on vscode\
  2. Then in the terminal, create a new virtual environment
  3. ENSURE YOU SOURCE THE ENVIRONMENT AFTER CREATING IT (. .venv/Scripts/activate). Do not miss the first dot
  4. Afterwards, install the pydantic library (pip install pydantic)
  5. Run pip freeze
  6. Redirect the output of pip freeze to a file called requirements.txt
  7. Check that the content matches this:
annotated-types==0.7.0
pydantic==2.11.7
pydantic_core==2.33.2
typing-inspection==0.4.1
typing_extensions==4.15.0

Section 4: Working with Modules and Packages

You do not need to write all your code in one single file. Python allows you to split your code into multiple files.

All the standard library modules you've imported so far are actual python files written by the Python maintainers.

You can write your own python files that can be imported, though they might not be added to the standard library, 🥲

from bank import deposit, withdraw
deposit(400)
withdraw(200)
from fraud_utils.detection import detect_fraud
detect_fraud(3000000000)
from bank import deposit
deposit(384)

Class Exercise on imports and modules

You will be grouped into teams to do this exercise

  1. Create a directory called hr_application
  2. Add the following folder and files
appraisal
    utils.py
    staff.py
    management.py
input_flows.py
main.py

Note that appraisal is a directory.

3. Create input_flows.py

  • Add a function to ask the user which type of employee to appraise (staff or management).
  • Add a function to display all available users of that type.
  • Add a function to select a user based on their ID.
  • Use the dictionary below as a temporary database
"employees" = {
    "staff": {
        "stf01": {
            "name": "Ogoni Marthia",
            "email": "[email protected]",
            "staffId": "stf01"
        },
        "stf02": {
            "name": "Lina Adeyemi",
            "email": "[email protected]",
            "staffId": "stf02"
        },
        "stf03": {
            "name": "Tunde Bakare",
            "email": "[email protected]",
            "staffId": "stf03"
        }
    },
    "management": {
        "mgmt01": {
            "name": "Carol Danvers",
            "email": "[email protected]",
            "staffId": "mgmt01"
        },
        "mgmt02": {
            "name": "Anayakoji Kafka",
            "email": "[email protected]",
            "staffId": "mgmt02"
        }
    }
}

3. Create input_flows.py

  • Add a function to ask the user which type of employee to appraise (staff or management).
  • Add a function to display all available users of that type.
  • Add a function to select a user based on their ID.

4. Create main.py

  • Import the necessary functions from the appraisal package.
  • Import the input collection functions from input_flows using absolute imports.
  • The app should:
    1. Ask which type of employee to appraise.
    2. Show available users for that type.
    3. Allow the user to select one.
    4. Run the appropriate appraisal function (appraise_staff or appraise_management) for the selected user.

5. Goal

  • Practice creating and organizing Python packages and modules.
  • Practice using absolute imports for external modules (input_flows) and relative imports within a package.
  • Understand modular design and separation of concerns: appraisal logic, utilities, input collection, and main execution flow.

LUNCH BREAK

Section 5: External Modules and APIs

We'll:

  • Import our own packages
  • Install external ones (requests, beautifulsoup4, pydantic)
  • Practice API requests with a Flask backend
import requests
print(requests.get("http://localhost:5000/users").json())
import requests
from bs4 import BeautifulSoup
html = "<html><body><h1>Hello!</h1></body></html>"
soup = BeautifulSoup(html, "html.parser")
print(soup.h1.text)
from pydantic import BaseModel
class User(BaseModel):
name: str
email: str
interest1: str
interest2: str
interest3: str
u = User(
name="Ada",
email="[email protected]",
interest1="Music",
interest2="Cooking",
interest3="Chess"
)
print(u)
import requests
BASE_URL = "http://localhost:5000"
resp = requests.get(f"{BASE_URL}/users")
print("Users:", resp.json())
# CLASS EXERCISE:
# Write a client for the Flask API
# 1. Seed users
# 2. Print them
# 3. Add a new user
# 4. Delete a user by ID
import requests
BASE_URL = "http://localhost:5000"
from flask import Flask, request, jsonify
import sqlite3
app = Flask(__name__)
DB_NAME = "class.db"
def init_db():
with sqlite3.connect(DB_NAME) as conn:
cur = conn.cursor()
cur.execute(
"""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL,
interest1 TEXT NOT NULL,
interest2 TEXT NOT NULL,
interest3 TEXT NOT NULL
)
"""
)
conn.commit()
def query_db(query, args=(), one=False):
with sqlite3.connect(DB_NAME) as conn:
conn.row_factory = sqlite3.Row
cur = conn.cursor()
cur.execute(query, args)
rv = cur.fetchall()
conn.commit()
return (rv[0] if rv else None) if one else rv
@app.get("/")
def root_route():
return "<h1>Hey guys</h1>"
@app.route("/seed", methods=["POST", "GET"])
def seed():
users = [
("Ada", "[email protected]", "Music", "Cooking", "Chess"),
("Musa", "[email protected]", "Football", "Traveling", "Movies"),
("Tomi", "[email protected]", "Reading", "Gaming", "Art"),
]
query_db("DELETE FROM users")
with sqlite3.connect(DB_NAME) as conn:
cur = conn.cursor()
cur.executemany(
"INSERT INTO users (name, email, interest1, interest2, interest3) VALUES (?, ?, ?, ?, ?)",
users,
)
conn.commit()
return jsonify({"message": "Users seeded!", "users": get_all_users()})
def get_all_users():
rows = query_db("SELECT * FROM users")
return [dict(row) for row in rows]
@app.route("/users", methods=["GET"])
def get_users():
return jsonify(get_all_users())
@app.route("/users", methods=["POST"])
def add_user():
data = request.get_json()
required = {"name", "email", "interest1", "interest2", "interest3"}
if not data or not required.issubset(data):
return jsonify({"error": "Missing one of: name, email, interest1-3"}), 400
query_db(
"INSERT INTO users (name, email, interest1, interest2, interest3) VALUES (?, ?, ?, ?, ?)",
(data["name"], data["email"], data["interest1"], data["interest2"], data["interest3"]),
)
return jsonify({"message": "User added", "users": get_all_users()}), 201
@app.route("/users/<int:user_id>", methods=["DELETE"])
def delete_user(user_id):
query_db("DELETE FROM users WHERE id = ?", (user_id,))
return jsonify({"message": f"User {user_id} deleted", "users": get_all_users()})
if __name__ == "__main__":
init_db()
app.run(debug=True)
def deposit(amount: int):
print(f"{amount} deposited successfully")
def withdraw(amount: int):
print(f"{amount} withdrawn successfully")
def transfer(amount: int):
print(f"{amount} transfered successfully")
# from banking_security_utils.hashing.sha256 import hash_data
# hash_data("my-sweet-sweet-password")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment