Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions prep-exercises/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Virtual Environment
venv/
env/
ENV/

# Python cache
__pycache__/
*.pyc
*.pyo
*.pyd

# mypy cache
.mypy_cache/

# IDE
.vscode/
.idea/
*.swp
*.swo

# OS
.DS_Store

25 changes: 25 additions & 0 deletions prep-exercises/classes_and_objects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
class Person:
def __init__(self, name: str, age: int, preferred_operating_system: str, address: str):
self.name = name
self.age = age
self.preferred_operating_system = preferred_operating_system
self.address = address

def is_adult(self) -> bool:
return self.age >= 18


imran = Person("Imran", 22, "Ubuntu", "123 Main St")
print(imran.name)
print(imran.address)
print(imran.is_adult())

eliza = Person("Eliza", 34, "Arch Linux", "456 Oak Ave")
print(eliza.name)
print(eliza.address)


def get_address(person: Person) -> str:
return person.address

print(get_address(imran))
25 changes: 25 additions & 0 deletions prep-exercises/dataclasses_exercise.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from dataclasses import dataclass
from datetime import date

@dataclass(frozen=True)
class Person:
name: str
date_of_birth: date
preferred_operating_system: str

def is_adult(self) -> bool:
current_date = date.today()
age = current_date.year - self.date_of_birth.year - (
(current_date.month, current_date.day) < (self.date_of_birth.month, self.date_of_birth.day)
)
return age >= 18

# Example usage:
imran = Person("Imran", date(2001, 5, 15), "Ubuntu")
print(imran)
print(imran.is_adult())

eliza = Person("Eliza", date(1990, 3, 20), "Arch Linux")
print(eliza)
print(eliza.is_adult())

70 changes: 70 additions & 0 deletions prep-exercises/enums_exercise.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from dataclasses import dataclass
from enum import Enum

class OperatingSystem(Enum):
MACOS = "macOS"
UBUNTU = "Ubuntu"
ARCH = "Arch Linux"

@dataclass
class Laptop:
id: int
manufacturer: str
model: str
screen_size_in_inches: float
operating_system: OperatingSystem

laptops = [
Laptop(id=1, manufacturer="Lenovo", model="ThinkPad", screen_size_in_inches=14, operating_system=OperatingSystem.ARCH),
Laptop(id=2, manufacturer="HP", model="Pavilion", screen_size_in_inches=15.6, operating_system=OperatingSystem.UBUNTU),
Laptop(id=3, manufacturer="Asus", model="ZenBook", screen_size_in_inches=13.3, operating_system=OperatingSystem.UBUNTU),
Laptop(id=4, manufacturer="Apple", model="MacBook Air", screen_size_in_inches=13, operating_system=OperatingSystem.MACOS),
]

@dataclass
class Person:
name: str
age: int
preferred_operating_system: OperatingSystem


name = input('Please enter your name: ')

age_input = input('Please enter your age: ')
try:
age = int(age_input)
except ValueError:
print(f"Error: '{age_input}' is not a valid age. Please enter a number.")
exit()

preferred_os_input = input('Please enter your preferred operating system: ')
try:
preferred_operating_system = OperatingSystem(preferred_os_input)
except ValueError:
available_options = ', '.join([os.value for os in OperatingSystem])
print(f"Error: '{preferred_os_input}' is not available. Please choose from: {available_options}")
exit()


person = Person(name=name, age=age, preferred_operating_system=preferred_operating_system)


number_of_available_laptops = sum(
1 for laptop in laptops if laptop.operating_system == person.preferred_operating_system
)

def offer_laptop_to_user() -> None:
offer = input('Would you like a laptop with this OS (yes / no) ? ')
if offer.lower() == "y" or offer.lower() == "yes":
print('Great! Please come on monday next week to collect your laptop')
else:
print('No problem. See you later.')

if number_of_available_laptops == 1:
print(f'There is {number_of_available_laptops} laptop available with your preferred operating system.')
offer_laptop_to_user()
elif number_of_available_laptops > 1:
print(f'There are {number_of_available_laptops} laptops available with your preferred operating system.')
offer_laptop_to_user()
else:
print('There are no laptops available with your preferred operating system.')
48 changes: 48 additions & 0 deletions prep-exercises/generics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from dataclasses import dataclass
from typing import List
from datetime import date

@dataclass(frozen=True)
class Person:
name: str
date_of_birth: date
children: List["Person"]

def age(self) -> int:
current_date = date.today()
age = current_date.year - self.date_of_birth.year - (
(current_date.month, current_date.day) < (self.date_of_birth.month, self.date_of_birth.day)
)
return age

# Calculate dates of birth based on current age
def years_ago(years: int) -> date:
today = date.today()
try:
return date(today.year - years, today.month, today.day)
except ValueError: # Handle leap year edge case (Feb 29)
return date(today.year - years, today.month, today.day - 1)

sara = Person(name="Sara", date_of_birth=years_ago(5), children=[])
ahmed = Person(name="Ahmed", date_of_birth=years_ago(8), children=[])

ali = Person(name="Ali", date_of_birth=years_ago(28), children=[sara])
aya = Person(name="Aya", date_of_birth=years_ago(32), children=[ahmed])

imran = Person(name="Imran", date_of_birth=years_ago(55), children=[ali, aya])

def print_family_tree(person: Person) -> None:
print(f"{person.name} ({person.age()})")
for child in person.children:
print(f" - {child.name} ({child.age()})")
for grandchild in child.children:
print(f" - {grandchild.name} ({grandchild.age()})")

def count_family_members(person: Person) -> int:
count = 1
for child in person.children:
count += count_family_members(child)
return count

print_family_tree(imran)
print(f"\nTotal family members: {count_family_members(imran)}")
40 changes: 40 additions & 0 deletions prep-exercises/inheritance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
class Parent:
def __init__(self, first_name: str, last_name: str):
self.first_name = first_name
self.last_name = last_name

def get_name(self) -> str:
return f"{self.first_name} {self.last_name}"


class Child(Parent):
def __init__(self, first_name: str, last_name: str):
super().__init__(first_name, last_name)
self.previous_last_names: list[str] = []

def change_last_name(self, last_name: str) -> None:
self.previous_last_names.append(self.last_name)
self.last_name = last_name

def get_full_name(self) -> str:
suffix = ""
if len(self.previous_last_names) > 0:
suffix = f" (née {self.previous_last_names[0]})"
return f"{self.first_name} {self.last_name}{suffix}"


print("Creating Child instance:")
person1 = Child("Sarah", "Johnson")
print(f"Name: {person1.get_name()}")
print(f"Full name: {person1.get_full_name()}")

print("\nChanging last name to Smith:")
person1.change_last_name("Smith")
print(f"Name: {person1.get_name()}")
print(f"Full name: {person1.get_full_name()}")

print("\nCreating Parent instance:")
person2 = Parent("Emma", "Wilson")
print(f"Name: {person2.get_name()}")

# person2.change_last_name("Brown") # Error: Parent doesn't have change_last_name method
22 changes: 22 additions & 0 deletions prep-exercises/methods.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from datetime import date

class Person:
def __init__(self, name: str, DoB: date, preferred_operating_system: str):
self.name = name
self.DoB = DoB
self.preferred_operating_system = preferred_operating_system

def is_adult(self) -> bool:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is a benefit or drawback of writing the function this way?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stores DoB (fixed), not age (changes over time), so is_adult() stays correct.
Rtores DoB (fixed), not age (changes over time), so is_adult() stays correct.

current_date = date.today()
age = current_date.year - self.DoB.year - (
(current_date.month, current_date.day) < (self.DoB.month, self.DoB.day)
)
return age >= 18



eliza = Person("Eliza", date(2010, 5, 15), "Arch Linux")
print(f"{eliza.name} is adult: {eliza.is_adult()}")

sara = Person("Sara", date(1995, 12, 20), "macOS")
print(f"{sara.name} is adult: {sara.is_adult()}")
1 change: 1 addition & 0 deletions prep-exercises/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mypy==1.18.2
34 changes: 34 additions & 0 deletions prep-exercises/type_checking_with_mypy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
def open_account(balances: dict[str, int], name: str, amount: int) -> None:
balances[name] = amount

def sum_balances(accounts: dict[str, int]) -> int:
total = 0
for name, pence in accounts.items():
print(f"{name} had balance {pence}")
total += pence
return total

def format_pence_as_string(total_pence: int) -> str:
if total_pence < 100:
return f"{total_pence}p"
pounds = int(total_pence / 100)
pence = total_pence % 100
return f"£{pounds}.{pence:02d}"

balances: dict[str, int] = {
"Sima": 700,
"Linn": 545,
"Georg": 831,
}

# Bugs fixed:
# 1. Added balances as first argument
# 2. Converted amounts to pence (int)
# 3. Fixed function name typo
open_account(balances, "Tobi", 913)
open_account(balances, "Olya", 713)

total_pence = sum_balances(balances)
total_string = format_pence_as_string(total_pence)

print(f"The bank accounts total {total_string}")
55 changes: 55 additions & 0 deletions prep-exercises/type_guided_refactorings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from dataclasses import dataclass
from typing import Optional

# Before refactoring - unclear types
def process_user_data(data):
name = data[0]
age = data[1]
email = data[2] if len(data) > 2 else None

if age < 18:
return None

return f"{name} ({age}): {email or 'no email'}"


# After refactoring - clear types
@dataclass
class User:
name: str
age: int
email: Optional[str] = None

def is_adult(user: User) -> bool:
return user.age >= 18

def format_user_info(user: User) -> str:
email_str = user.email if user.email else "no email"
return f"{user.name} ({user.age}): {email_str}"

def process_user(user: User) -> Optional[str]:
if not is_adult(user):
return None
return format_user_info(user)


# Testing old version
print(process_user_data(("Alice", 25, "alice@example.com")))
print(process_user_data(("Bob", 16)))

# Testing new version
user1 = User("Alice", 25, "alice@example.com")
user2 = User("Bob", 16)

print(process_user(user1))
print(process_user(user2))


# Another example
def get_value(key: str, data: dict[str, int]) -> Optional[int]:
return data.get(key)

sample_data = {"a": 1, "b": 2}
result = get_value("a", sample_data)
if result is not None:
print(f"Found: {result}")
28 changes: 28 additions & 0 deletions prep-exercises/why_we_use_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
def half(value):
return value / 2

def double(value):
return value * 2

def second(value):
return value[1]

# Prediction: double("22") will return "2222" because * operator repeats strings
print(double("22"))

# Testing other cases
print(half(22))
# print(half("hello")) # This will error
# print(half("22")) # This will error

print(double(22))
print(double("hello"))

# print(second(22)) # This will error
print(second("hello"))
print(second("22"))

def double_bug(number):
return number * 2

print(double_bug(10)) # Returns 20 (correctly doubles the number)
Loading