Skip to content
24 changes: 24 additions & 0 deletions prep exercises/Classes and objects exercise 1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class Person:
def __init__(self, name: str, age: int, preferred_operating_system: str):
self.name = name
self.age = age
self.preferred_operating_system = preferred_operating_system

imran = Person("Imran", 22, "Ubuntu")
print(imran.name)
# print(imran.address) # error: "Person" has no attribute "address" [attr-defined]

eliza = Person("Eliza", 34, "Arch Linux")
print(eliza.name)
# print(eliza.address) # error: "Person" has no attribute "address" [attr-defined]

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

print(is_adult(imran))

def no_attribute_exists(person: Person):
print(person.dob)

# mypy detects that dob attribute doesn't exist.
# error: "Person" has no attribute "dob" [attr-defined]
23 changes: 23 additions & 0 deletions prep exercises/Dataclasses exercise.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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):
today = date.today()
age = today.year - self.date_of_birth.year

if (today.month, today.day) < (self.date_of_birth.month, self.date_of_birth.day):
age -= 1

return age >= 18

imran = Person("Imran", date(2004, 6, 28), "Ubuntu") # We can call this constructor - @dataclass generated it for us.
print(imran) # Prints Person(name='Imran', age=22, preferred_operating_system='Ubuntu')

imran2 = Person("Imran", date(2004, 6, 28), "Ubuntu")
print(imran == imran2) # Prints True
105 changes: 105 additions & 0 deletions prep exercises/Enums exercise.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from dataclasses import dataclass
from enum import Enum
from typing import List
import sys

class OperatingSystem(Enum):
MACOS = "macos"
ARCH = "arch linux"
UBUNTU = "ubuntu"

@dataclass(frozen=True)
class Person:
name: str
age: int
preferred_operating_system: OperatingSystem


@dataclass(frozen=True)
class Laptop:
id: int
manufacturer: str
model: str
screen_size_in_inches: float
operating_system: OperatingSystem


def find_possible_laptops(laptops: List[Laptop], person: Person) -> List[Laptop]:
possible_laptops = []
for laptop in laptops:
if laptop.operating_system == person.preferred_operating_system:
possible_laptops.append(laptop)
return possible_laptops


people = [
Person(name="Imran", age=22, preferred_operating_system=OperatingSystem.UBUNTU),
Person(name="Eliza", age=34, preferred_operating_system=OperatingSystem.ARCH),
]

laptops = [
Laptop(id=1, manufacturer="Dell", model="XPS", screen_size_in_inches=13, operating_system=OperatingSystem.ARCH),
Laptop(id=2, manufacturer="Dell", model="XPS", screen_size_in_inches=15, operating_system=OperatingSystem.UBUNTU),
Laptop(id=3, manufacturer="Dell", model="XPS", screen_size_in_inches=15, operating_system=OperatingSystem.UBUNTU),
Laptop(id=4, manufacturer="Apple", model="macBook", screen_size_in_inches=13, operating_system=OperatingSystem.MACOS),
]

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

def get_age() -> int:
while True:
try:
age_string = input("Please enter your age, you must be >= 18 years old: ")
age = int(age_string)

if age < 18:
print("You must be 18+.")
continue

return age
except ValueError:
print("Please enter a valid number.")

age = get_age()

def get_preferred_operating_system() -> OperatingSystem:
while True:
preferred_operating_system_input = input("Your preferred operating system (macOS, Ubuntu or Arch Linux): ")

try:
normalised_value = preferred_operating_system_input.strip().lower()
operating_system = OperatingSystem(normalised_value)
return operating_system
except ValueError:
print(f"Sorry, we don't have {preferred_operating_system_input}. Please choose from macOS, Ubuntu or Arch Linux.")

operating_system = get_preferred_operating_system()

person = Person(name=name, age=age, preferred_operating_system=operating_system)
people.append(person)

def find_laptop() -> None:
matching_laptops = []
non_matching_laptops = []

for laptop in laptops:
if laptop.operating_system == person.preferred_operating_system:
matching_laptops.append(laptop)
else:
non_matching_laptops.append(laptop)

if matching_laptops:
count = len(matching_laptops)
print(f"Congratulations, we have found {count} matching laptops!")

other_laptops_count = len(non_matching_laptops)

if other_laptops_count > count:
print(f"We have found {other_laptops_count} laptops with other operating systems. You might be interested in switching to a different operating system.")

sys.exit(0)
else:
sys.exit("Sorry, we couldn't find any matching laptops!")


find_laptop()
20 changes: 20 additions & 0 deletions prep exercises/Generics exercise.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from dataclasses import dataclass
from typing import List

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

fatma = Person(name="Fatma", age=7, children=[])
aisha = Person(name="Aisha", age=11, children=[])

imran = Person(name="Imran", age=32, children=[fatma, aisha])

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

print_family_tree(imran)
37 changes: 37 additions & 0 deletions prep exercises/Inheritance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
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 = []

def change_last_name(self, last_name) -> 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}"

person1 = Child("Elizaveta", "Alekseeva")
print(person1.get_name()) # Should print Elizaveta Actual: Elizaveta Alekseeva
print(person1.get_full_name()) # Should print Elizaveta Alekseeva
person1.change_last_name("Tyurina")
print(person1.get_name()) # Should print Elizaveta Actual: Elizaveta Tyurina
print(person1.get_full_name()) # Should print Elizaveta Alekseeva (née Tyurina) Actual: Elizaveta Tyurina (née Alekseeva)

person2 = Parent("Elizaveta", "Alekseeva")
print(person2.get_name()) # Should print Elizaveta Alekseeva
# print(person2.get_full_name()) # Method doesn't exist because only Child class has it.
# person2.change_last_name("Tyurina") # Method doesn't exist because only Child class has it.
print(person2.get_name()) # Should print Elizaveta Alekseeva
# print(person2.get_full_name()) # Method doesn't exist because only Child class has it.
4 changes: 4 additions & 0 deletions prep exercises/Methods exercise 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

good expalantion

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
The advantage of a method as a opposed to freestanding function is that it works on the instance of a type.
The freestanding function knows nothing about the instance of the type because it doesn't belong to a type.
On an instance, the method can influence the internal states.
However freestanding function can only work on public APIs whereas instance methods have access to internal APIs.
19 changes: 19 additions & 0 deletions prep exercises/Methods exercise 2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from datetime import date

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

def is_adult(self):
today = date.today()
age = today.year - self.date_of_birth.year

if (today.month, today.day) < (self.date_of_birth.month, self.date_of_birth.day):
age -= 1

return age >= 18

imran = Person("Imran", date(2004, 6, 28), "Ubuntu")
print(imran.is_adult())
34 changes: 34 additions & 0 deletions prep exercises/Type checking with mypy exercise.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
def open_account(balances: dict[str, float], name: str, amount: float) -> None:
balances[name] = amount

def sum_balances(accounts: dict[str, float]) -> float:
total: float = 0

for name, pence in accounts.items():
print(f"{name} had balance {pence}")
total += pence

return total

def format_pence_as_string(total_pence: float) -> str:
if total_pence < 100:
return f"{total_pence}p"

pounds = int(total_pence / 100)
pence = int(total_pence % 100)

return f"£{pounds}.{pence:02d}"

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

open_account(balances, "Tobi", 9.13)
open_account(balances, "Olya", 7.13)

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

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

@dataclass(frozen=True)
class Person:
name: str
age: int
preferred_operating_systems: List[str]


@dataclass(frozen=True)
class Laptop:
id: int
manufacturer: str
model: str
screen_size_in_inches: float
operating_system: str


def find_possible_laptops(laptops: List[Laptop], person: Person) -> List[Laptop]:
possible_laptops = []
for laptop in laptops:
if laptop.operating_system in person.preferred_operating_systems:
possible_laptops.append(laptop)
return possible_laptops


people = [
Person(name="Imran", age=22, preferred_operating_systems=["Ubuntu"]),
Person(name="Eliza", age=34, preferred_operating_systems=["Arch Linux"]),
]

laptops = [
Laptop(id=1, manufacturer="Dell", model="XPS", screen_size_in_inches=13, operating_system="Arch Linux"),
Laptop(id=2, manufacturer="Dell", model="XPS", screen_size_in_inches=15, operating_system="Ubuntu"),
Laptop(id=3, manufacturer="Dell", model="XPS", screen_size_in_inches=15, operating_system="ubuntu"),
Laptop(id=4, manufacturer="Apple", model="macBook", screen_size_in_inches=13, operating_system="macOS"),
]

for person in people:
possible_laptops = find_possible_laptops(laptops, person)
print(f"Possible laptops for {person.name}: {possible_laptops}")
9 changes: 9 additions & 0 deletions prep exercises/Why we use types exercise 1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
def double(value):
return value * 2

# I predict that double("22") will produce 22.0

result = double("22")
print(result) # Actual result is 2222

# This is because the string is duplicated twice, "22" + "22" = "22" * 2, it is repeating the a sequence of string.
15 changes: 15 additions & 0 deletions prep exercises/Why we use types exercise 2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
def double(number):
return number * 3

print(double(10))

# There are two possible ways to fix the bug.

# 1. The function name is wrong, it needs to be named to triple
# 2. Or the function name is correct but the calculation is wrong, it should be return * 2

def double(number):
return number * 2

value = 10
print(double(value))
Loading