Skip to content

Instantly share code, notes, and snippets.

@vicradon
Last active August 28, 2025 09:37
Show Gist options
  • Select an option

  • Save vicradon/5a8530545d63cc6037689c7a6143433a to your computer and use it in GitHub Desktop.

Select an option

Save vicradon/5a8530545d63cc6037689c7a6143433a to your computer and use it in GitHub Desktop.

Python Intro

An introductory course to Python programming

print("What's good, world?")
my_name = "Mr. Brother Man"
my_age = 42
my_interests = ["Anime", "Archery", "Skating"]
print("My name is", my_name, "and I am", my_age, "and my interests are", *my_interests)
message = f"My name is {my_name} and I am {my_age} and my interests are {my_interests[0]}, {my_interests[1]}, and {my_interests[2]}"
print(message)

Basic Explanation on Variables

my_name is a string. It is used for things like names, and natural language
my_age is an integer. It is used for whole numbers, both negative and positive
my_interests is a list. It is used for grouping/ordering related variables

# floats
PI = 3.14
accelertion_due_to_gravity = 9.81
print("Acceleration due to gravity on earth is", accelertion_due_to_gravity)
# bools
tech_bros_are_fraudsters = False
is_raining_season = True
print("Are techbros frauds?", tech_bros_are_fraudsters)
print("Are we in raning season?", is_raining_season)
# dictionary (hashmap)
account_names_to_numbers = {
"Ajibola Philips": 3110000000,
"Moses Franklin": 3011000000,
"Ijeoma Peters": 3021000000,
}
students = dict()
students["you"] = {"name": "", "age": 26, "occupation": "tech bro"}
print(type(account_names_to_numbers), type(students))
# Tuple
random_vector = (5, 45.0) # vector -> magnitude, direction
mercy_details = ("Mercy Franklin", 25, False) # name, age, is_single
# Set (hashset)
account_numbers = set([3110000000, 3110000000, 3021000000])
print("Account numbers", account_numbers)
account_numbers = {3110000000, 3021000000}

Class Exercise on Data Types and Structures

Task: Write a dictionary that has your name, twitter_handle, favorite constants, and what not. Goal: To help you understand the different data types you can work with in Python

my_internet_profile = {
    "name": None,                          # string
    "twitter_handle": None,                # string
    "favorite_physics_constant": None,     # float
    "age": None,                           # integer (you can lie about this lol)
    "finished_uni": None,                  # boolean
    "hobbies": None,                       # tuple
    "skills": None,                        # list
    "personal_quotes": None,               # set (one or two)
    "contact_info": {                      # dictionary (nested)
        "phone_number": None,              # integer (you can put a fake one)
        "email": None,                     # string (you can put a fake one)
        "website": None,                   # string
    },
}

print(my_internet_profile)
input("What is your name: ")
name = input("What is your name: ")
print(name)

Collecting Inputs Class Exercise

Task: Write a program that collects your name and interests Goal: To teach you how to implement data collection and casting

name = input("What is your name: ")
age = int(input("What"))
interest1 = input("What is your number 1 interest: ")
interest2 = input("What is ")
interest3 = input("What ")

combined = f"Your name is {name} and"

print(combined)
name = input("What is your name: ")
age = input("What")
interest1 = input("What is your number 1 interest: ")
interest2 = input("What is ")
interest3 = input("What ")
combined = f"Your name is {name} and"
print(combined)

Loops Intro

Notice how you needed to define interest1, interest2, and interest3 manually? What if there was a way to write less code? That is where a for-loop comes in.

name = input("What is your name: ")
age = input("What is your age: ")
interests = []
for i in range(3):
interest = input(f"What is your number {i} interest: ")
interests.append(interest)
interests_string = ", ".join(interests)
combined = f"Your name is {name} and you are {age} years old with interests in {interests_string}"
print(combined)

Parts of a loop

  1. increment variable
  2. iterator/iteration
  3. repeated code

In the previous loop, i is the increment variable,
range(3) is the iterator and the snippet below is the repeated code

  interest = input(f"What is your number {i} interest: ")
  interests.append(interest)
name = input("What is your name: ")
age = input("What is your age: ")
interests = []
i = 1 # increment variable
while i <= 3: #
interest = input(f"What is your number {i} interest: ")
interests.append(interest)
i += 1 # iteration
interests_string = ", ".join(interests)
combined = f"Your name is {name} and you are {age} years old with interests in {interests_string}"
print(combined)

Functions Intro

Functions allow you to group logic. So the input collector you’ve built so far can be stored as a function that can be called anytime.

def collect_input():
name = input("What is your name: ")
age = input("What is your age: ")
interests = []
for i in range(3):
interest = input(f"What is your number {i} interest: ")
interests.append(interest)
interests_string = ", ".join(interests)
return f"Your name is {name} and you are {age} years old with interests in {interests_string}"
collect_input()
def add(num1, num2):
return num1 + num2
number_sum = add(2, 3)
print(number_sum)
'''
num1 is the first parameter
num2 is the second parameter
2 is the first argument (things passed to functions)
3 is the second argument
return is a keyword for returning from a function
'''
def subtract(num1, num2):
return num1 - num2
difference = subtract(7, 3)
print(difference)
def divide(num1, num2):
return num1/num2
print("5 divide by 2 is", divide(5,2), "\n\n")
print("3 divide by 0 is", divide(3, 0)) # throws error
def divide(num1, num2):
try:
return num1/num2
except Exception as e:
print(str(e))
print("5 divide by 2 is", divide(5,2))
print("3 divide by 0 is", divide(3, 0)) # throws error
def divide(num1, num2):
try:
return num1/num2
except ZeroDivisionError:
print("You attempted to divide by zero")
print("5 divide by 2 is", divide(5,2))
print("3 divide by 0 is", divide(3, 0)) # throws error

Python Classes

A Class is a more advanced way to group several functions and related logic in one place

class Calculator:
def __init__(self):
pass
def add(self, a, b):
return a + b
def subtract(self, num1, num2):
return num1 - num2
def divide(self, num1, num2):
try:
return num1/num2
except ZeroDivisionError:
print("You attempted to divide by zero")
def advanced_addition(self, *args):
"""This takes any number of arguments (numbers) and returns their sum."""
result = 0
for num in args:
result = result + num
return result
calculator = Calculator()
sum1 = calculator.add(4, 5)
diff = calculator.divide(9, 2)
sum2 = calculator.advanced_addition(4, 5, 6)
print(sum1)
print(diff)
print(sum2)

Parts of a class

  1. name - Calculator
  2. self - a secret first argument that the snitch, Python, passes to every function defined within a class to make them methods.
  3. init - a dunder method that python invokes when initializing a class. There are others like str, __converting a class to string
  4. Methods
class Calculator:
def __init__(self, log_results: bool = False):
self.log_results = log_results
def add(self, num1: float, num2: float):
result = num1 + num2
if self.log_results:
print(f"The sum of {num1} and {num2} is {result}")
return result
calculator = Calculator(log_results=True)
calculator.add(4, 5) # no need for print

Classes are simply ways to create your own type of logic

So you can create behaviors, like how printing occurs, how data is accessed, in very specific ways.

class Calculator:
def __init__(self, log_results: bool = False):
self.log_results = log_results
self.history = {
"addition": [],
"subtraction": [],
"division": [],
"multiplication": []
}
def add(self, num1: float, num2: float):
result = num1 + num2
if self.log_results:
print(f"The sum of {num1} and {num2} is {result}")
self.history["addition"].append((num1, num2, result))
return result
calculator = Calculator()
calculator.add(4, 5)
calculator.add(20, 25)
calculator.add(-20, 30)
print(calculator.history)

Practice Keeping State

Task: implement a new class called CalculatorHistory, then initialize it within the init method of the calculator class. Purpose: To make it possible to use object accessors to access history methods rather than dictionary accessors Starting point:

class CalculatorHistory:
    def __init__(self):
        # add the remaining
        self.addition = []

    def __str__(self):
        return f"""
            Addition: {self.addition}
        """

class Calculator:
    def __init__(self, log_results: bool = False):
        self.log_results = log_results
        self.history = {
            "addition": []
        }

    def add(self, num1: float, num2: float):
        result = num1 + num2

        if self.log_results:
            print(f"The sum of {num1} and {num2} is {result}")

        self.history["addition"].append((num1, num2, result))
        # goal 
        # self.history.addition.append((num1, num2, result))

        return result


calculator = Calculator()

calculator.add(4, 5)
calculator.add(20, 25)
calculator.add(-20, 30)

print(calculator.history)

Object Oriented Programming (OOP)

Python allows you to implement some object oriented programming principles like inheritance, encapsulation, and polymorphism, with other principles (abstraction) supported to some degree.

OOP princples help you write leaner, more organised code.

In this module, you will implement some OOP principles on the calculator exmple.

import math
class Calculator:
def __init__(self, log_results: bool = False):
self.log_results = log_results
def add(self, num1: float, num2: float):
result = num1 + num2
if self.log_results:
print(f"The sum of {num1} and {num2} is {result}")
return result
def subtract(self, num1, num2):
return num1 - num2
def divide(self, num1, num2):
try:
return num1/num2
except ZeroDivisionError:
print("You attempted to divide by zero")
def advanced_addition(self, *args):
"""This takes any number of arguments (numbers) and returns their sum."""
result = 0
for num in args:
result = result + num
return result
class ScientificCalculator(Calculator):
def __init__(self, log_results: bool = False):
super().__init__(log_results)
def sin(self, x):
return math.sin(x)
def cos(self, x):
return math.cos(x)
def tan(self, x):
return math.tan(x)
def log(self, x, base=10):
result = math.log(x, base)
if self.log_results:
print(f"The log to base 10 of {x} is: {result}")
return result
def nat_log(self, x):
return math.log(x, math.e)
def factorial(self, n):
return math.factorial(n)
sci_calc = ScientificCalculator(log_results=True)
sci_calc.add(4, 5) # from the main calculator
sci_calc.log(100)
"""
Encapsulation equals method/variable hiding
In Python, we are all adults here
So if you see underscores, don't use that method or accesor
Single underscore _something -> still accessible
Double underscores __something -> not accessible directly
"""
import math
class Calculator:
def __init__(self, log_results: bool = False):
self.__log_results = log_results
def enable_logging(self):
self.__log_results = True
def disable_logging(self):
self.__log_results = False
@property
def is_logging_enabled(self):
return self.__log_results
def add(self, num1: float, num2: float):
result = num1 + num2
if self.is_logging_enabled:
print(f"The sum of {num1} and {num2} is {result}")
return result
class ScientificCalculator(Calculator):
def __init__(self, log_results: bool = False):
super().__init__(log_results)
def log(self, x, base=10):
result = math.log(x, base)
if self.is_logging_enabled:
print(f"The log to base 10 of {x} is: {result}")
return result
sci_calc = ScientificCalculator()
sci_calc.__log_results = True
print(sci_calc.is_logging_enabled)
sci_calc.add(3, 4)
sci_calc.enable_logging()
print(sci_calc.is_logging_enabled)
sci_calc.add(3, 4)
"""
Polymorphism means one accessor, many implementation
Here, the `add` method is implemented both on the Calculator and ScientificCalculator classes
The former has a basic implementation while the later has a more involved implementation
"""
class Calculator:
def __init__(self, log_results: bool = True):
self.__log_results = log_results
def enable_logging(self):
self.__log_results = True
def disable_logging(self):
self.__log_results = False
@property
def is_logging_enabled(self):
return self.__log_results
def add(self, num1: float, num2: float):
result = num1 + num2
if self.is_logging_enabled:
print(f"The sum of {num1} and {num2} is {result}")
return result
class ScientificCalculator(Calculator):
def __init__(self, log_results: bool = True):
super().__init__(log_results)
def add(self, *nums):
result = sum(nums)
if self.is_logging_enabled:
stringified_nums = "The sum of "
for i in range(len(nums) - 1):
stringified_nums += f"{nums[i]}, "
stringified_nums += f"and {nums[-1]}" # last element
print(f"The sum of {stringified_nums} is {result}")
return result
normal_calc = Calculator()
sci_calc = ScientificCalculator()
normal_calc.add(3, 4)
sci_calc.add(3, 4, 5)
"""In Abstraction, we define a base template every class must implement.
Ignoring sub class inheritance, for example, the scientific calculator, if multiple defined classes implement the AbstractCalculator,
they must define the add, subtract, multiply, and divide methods.
"""
from abc import ABC, abstractmethod
from fractions import Fraction
class AbstractCalculator(ABC):
@abstractmethod
def add(self, num1, num2):
pass
@abstractmethod
def subtract(self, num1, num2):
pass
@abstractmethod
def multiply(self, num1, num2):
pass
@abstractmethod
def divide(self, num1, num2):
pass
class BasicCalculator(AbstractCalculator):
def add(self, num1, num2):
return num1 + num2
def subtract(self, num1, num2):
return num1 - num2
def multiply(self, num1, num2):
return num1 * num2
def divide(self, num1, num2):
if num2 == 0:
raise ValueError("Cannot divide by zero")
return num1 / num2
class RoundingCalculator(AbstractCalculator):
"""Always rounds to 2 decimal places (useful in finance)."""
def add(self, num1, num2):
return round(num1 + num2, 2)
def subtract(self, num1, num2):
return round(num1 - num2, 2)
def multiply(self, num1, num2):
return round(num1 * num2, 2)
def divide(self, num1, num2):
if num2 == 0:
raise ValueError("Cannot divide by zero")
return round(num1 / num2, 2)
class FractionCalculator(AbstractCalculator):
"""Uses fractions for exact rational math."""
def add(self, num1, num2):
return Fraction(num1) + Fraction(num2)
def subtract(self, num1, num2):
return Fraction(num1) - Fraction(num2)
def multiply(self, num1, num2):
return Fraction(num1) * Fraction(num2)
def divide(self, num1, num2):
if num2 == 0:
raise ValueError("Cannot divide by zero")
return Fraction(num1) / Fraction(num2)
calculators = [
BasicCalculator(),
RoundingCalculator(),
FractionCalculator()
]
for calc in calculators:
print(f"\n{calc.__class__.__name__}")
print("Add:", calc.add(7, 3))
print("Subtract:", calc.subtract(7, 3))
print("Multiply:", calc.multiply(7, 3))
print("Divide:", calc.divide(7, 3))
import locale
from abc import ABC, abstractmethod
try:
locale.setlocale(locale.LC_ALL, "en_NG.UTF-8")
except locale.Error:
locale.setlocale(locale.LC_ALL, "")
def format_currency(amount: float) -> str:
formatted = locale.format_string("%0.2f", amount, grouping=True)
return f"₦{formatted}"
class Payment(ABC):
@abstractmethod
def pay(self, amount: float):
pass
class VerveCardPayment(Payment):
def __init__(self, card_number: str, pin: str):
self.card_number = card_number
self.pin = pin
def pay(self, amount: float):
print(f"Charging {format_currency(amount)} to Verve card {self.card_number[-4:]}...")
class MomoPayment(Payment):
def __init__(self, phone_number: str):
self.phone_number = phone_number
def pay(self, amount: float):
print(f"Processing MoMo payment of {format_currency(amount)} from {self.phone_number}...")
class CryptoPayment(Payment):
def __init__(self, wallet_address: str):
self.wallet_address = wallet_address
def pay(self, amount: float):
print(f"Sending crypto payment of {format_currency(amount)} to wallet {self.wallet_address[:6]}...")
def checkout(payment_method: Payment, amount: float):
payment_method.pay(amount)
checkout(VerveCardPayment("1234567890123456", "123"), 10000.0)
checkout(MomoPayment("810548xxxx"), 23_435)
checkout(CryptoPayment("0xAbCdEfGhIjKlMnOpQrStUvWxYz"), 2_000_000.0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment