An introductory course to Python programming
-
-
Save vicradon/5a8530545d63cc6037689c7a6143433a to your computer and use it in GitHub Desktop.
Python runs in so many environments:
- On windows via the CMD, Powershell, or IDLE
- On anaconda via Jupyter Notebook or JupyterLab
- On Google Colab
- On Linux (default installed on Ubuntu)
- On macOS via Terminal or IDEs like PyCharm and VS Code
- On web browsers using platforms like Repl.it, Trinket, or Brython
- On mobile devices using apps like Pydroid (Android) or Pythonista (iOS)
- Embedded in other applications, for example as scripting in Blender, GIMP, or Autodesk Maya
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) |
# 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} |
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) |
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) |
You can define branching rules in your codebase using conditionals. So if a particular condition is true or false, it can determine if certains blocks/lines of code will be executed.
Conditionals exist in many places, but are majorly implemented with if, elif, and else.
"""Run once example"""
a = 3
while a < 4:
print("do something")
a += 1
while True:
print("Welp, it's over")
"""In this conditionals example, you collect the distance ran by a user and prints message to them. | |
Ideally, you to guard against wrong input, since you are casting immediately to float (from string). | |
You can replace line 15 with the try catch below to guard against wrong input (don't forget the "import sys") | |
import sys | |
try: | |
distance = float(input("Enter distance run (in km): ")) | |
except: | |
print("why did you enter a non-number?, sigh") | |
sys.exit(1) | |
""" | |
distance = float(input("Enter distance run (in km): ")) | |
MARATHON_DISTANCE = 42.195 | |
# simple if | |
if distance >= 5: | |
print("Yay, you ran at least 5K!") | |
# if/else | |
if distance < 1: | |
print("That's basically a warm-up jog 😅") | |
else: | |
print("Nice effort!") | |
# if/elif/else chain | |
if distance < 5: | |
print("Keep going, you'll reach 5K soon.") | |
if distance < 10: | |
print("Almost a quarter marathon!") | |
elif distance < 22: | |
print("Almost a half marathon!") | |
elif distance < MARATHON_DISTANCE: | |
print("You're close to a full marathon!") | |
elif distance == MARATHON_DISTANCE: | |
print("👏 Congrats, you completed a marathon!") | |
else: | |
print("You're god-level, you exceeded a marathon by", distance - MARATHON_DISTANCE, "km") | |
# nested if | |
if distance > 10: | |
if distance < 15: | |
print("You're in the 10-15km range, solid training ground!") | |
elif distance < 20: | |
print("You're in the 15-20km range, you're a strong one!") | |
# logical operators | |
if distance >= 5 and distance <= 10: | |
print("That's a nice middle-distance run.") | |
if distance == MARATHON_DISTANCE/2 or distance == MARATHON_DISTANCE/4: | |
print("Exact amounts eh?") |
Class Exercise on Conditionals
Task: Write a program that: Asks the user for their exam score (0–100). Uses conditionals to print their grade:
70 and above → "A" 60–69 → "B" 50–59 → "C" 40–49 → "D" below 40 → "F"
Goal: To help you learn how to branch logic with if/elif/else.
score = int(input("Enter your exam score (0-100): ")) | |
# write your if/elif/else statements here | |
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) |
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) |
You are given a list of account numbers:
accounts = ["1234567890", "9876543210", "5555555555", "1111222233"]
Task:
- Ask the user to enter an account number.
- Use a for loop to check if the account number exists in the list.
- If it exists, print "200 - Account found".
- Otherwise, print "404 - Account not found"
# Exercise 1 - For Loop | |
accounts = ["1234567890", "9876543210", "5555555555", "1111222233"] | |
account_to_search = input("Enter account number to search: ") | |
# TODO: use a for loop to check if search_account is in accounts | |
# Hint: loop through accounts and compare the `account_to_search` to the looped element |
Banks only accept 10-digit numeric account numbers.
Task:
- Ask the user to enter their account number.
- Keep asking until they provide a valid 10-digit number (all numbers, length 10).
- Once valid, print "Account number accepted".
Goal: To practice using for loops to search lists, and while loops for input validation.
while True: | |
account_number = input("Enter your 10-digit account number: ") | |
# TODO: check that it is all digits and has length 10 | |
# If valid -> break loop, else keep asking |
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 |
Write a function that finds the min and max number in a list of numbers
Your function should:
- Take several inputs as arguments find_min_max(arg1, arg2, arg3, …, argn)
- Finds the minimum and maximum number
- Returns both of them as a tuple (minimum, maximum)
Write a few cases to verify that it works: [4, 7, 2, 9, 0, 3] [-9, -12, 38, 489, 38, 2, -2] [3903, 28, math.e, -90]
def find_min_max(*args): | |
min_num = float("-inf") # smallest possible number in python | |
max_num = float("inf") # largest possible number in python | |
return (min_num, max_num) | |
# TODO: write test cases and pass them to the function. Print the results |
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) |
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 |
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) |
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)
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) |
Task: Create a simple BankAccount class. It should store the account holder's name and balance. It should inherit from the AbstractBankAccount abstract class
It should have methods:
deposit(amount) -> adds to balance withdraw(amount) -> removes from balance (but not below 0) str() -> prints account details
Then Create a subclass called SavingsAccount that: Inherits from BankAccount Adds a method add_interest(rate) that increases the balance by a percentage
Goal: To practice creating your own classes, methods, inheritance, and state management.
class BankAccount: | |
def __init__(self, name, balance=0): | |
self.name = name | |
self.balance = balance | |
def deposit(self, amount): | |
pass # implement this | |
def withdraw(self, amount): | |
pass # implement this | |
def __str__(self): | |
return f"{self.name} has a balance of {self.balance}" | |
# TODO: Create a SavingsAccount class that inherits from BankAccount | |
# Example usage | |
account = BankAccount("Alice", 1000) | |
print(account) |