From d51f5d407c89e5e577852d80d6c9bd8f71a29306 Mon Sep 17 00:00:00 2001 From: Mikiyas-STP Date: Thu, 16 Oct 2025 19:13:53 +0100 Subject: [PATCH 01/13] exercise 1 --- prep_exercises/exercise1.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 prep_exercises/exercise1.py diff --git a/prep_exercises/exercise1.py b/prep_exercises/exercise1.py new file mode 100644 index 0000000..af99eef --- /dev/null +++ b/prep_exercises/exercise1.py @@ -0,0 +1,18 @@ +def double(value): + return value * 2 + +print(double("22")) +#Predict what double("22") will do + +#Answer +#"2222" because "22" will be treated as a string and then the program just duplicates it and return 2222 with a string value + +def double(number): + return number * 3 + +print(double(10)) + +#find the bug +#answer +#This is a logic error instead of giving the double it returns the triple of the number +#we can improve it by changing 3 by 2 since it doesnt have any type error From 591f7d2740ecf3765c40fa1f820eb22a76169cc4 Mon Sep 17 00:00:00 2001 From: Mikiyas-STP Date: Thu, 16 Oct 2025 19:23:09 +0100 Subject: [PATCH 02/13] exercisetwo type annotation --- prep_exercises/exercise2typeannotation.py | 31 +++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 prep_exercises/exercise2typeannotation.py diff --git a/prep_exercises/exercise2typeannotation.py b/prep_exercises/exercise2typeannotation.py new file mode 100644 index 0000000..31d70e4 --- /dev/null +++ b/prep_exercises/exercise2typeannotation.py @@ -0,0 +1,31 @@ +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 = total_pence // 100 + pence = total_pence % 100 + return f"£{pounds}.{pence:02d}" + + +balances = { + "Sima": 700, + "Linn": 545, + "Georg": 831, +} + +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}") From 8ab46a1e8c18de285b62830a6b19dade5abc4072 Mon Sep 17 00:00:00 2001 From: Mikiyas-STP Date: Thu, 16 Oct 2025 19:33:00 +0100 Subject: [PATCH 03/13] checking with mypy --- prep_exercises/exercise3.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 prep_exercises/exercise3.py diff --git a/prep_exercises/exercise3.py b/prep_exercises/exercise3.py new file mode 100644 index 0000000..e49f760 --- /dev/null +++ b/prep_exercises/exercise3.py @@ -0,0 +1,29 @@ +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) + +eliza = Person("Eliza", 34, "Arch Linux") +print(eliza.name) +print(eliza.address) + +#imran.address and eliza.address is not valid attribute +#error: "Person" has no attribute "address" + + +def is_adult(person: Person) -> bool: + return person.age >= 18 + +print(is_adult(imran)) +#ok + +def get_city(person: Person) -> str: + return person.city +#person has no attribute city. + +#to run mypy mypy ./***.py From ad93aba34f1cb14796cc5efc14f5755b1f070984 Mon Sep 17 00:00:00 2001 From: Mikiyas-STP Date: Thu, 16 Oct 2025 19:43:02 +0100 Subject: [PATCH 04/13] exercise4 free function vs methods --- prep_exercises/exercise4.py | 42 +++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 prep_exercises/exercise4.py diff --git a/prep_exercises/exercise4.py b/prep_exercises/exercise4.py new file mode 100644 index 0000000..ac73881 --- /dev/null +++ b/prep_exercises/exercise4.py @@ -0,0 +1,42 @@ +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 + + def is_adult(self): + return self.age >= 18 + +imran = Person("Imran", 22, "Ubuntu") +print(imran.is_adult()) + +#✍️exercise +#Think of the advantages of using methods instead of free functions. Write them down in your notebook. + + +#answer +#Reusability +#Polymorphism – Different subclasses can override the same method name with different behaviour. + + +#exercise +#Change the Person class to take a date of birth (using the standard library’s datetime.date class) and store it in a field instead of age. +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) -> bool: + 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 +#example +imra = Person("Imra", date(2003, 7, 14), "Ubuntu") +eliz = Person("Eliz", date(1991, 3, 25), "Arch Linux") + +print(imra.is_adult()) +print(eliz.is_adult()) From e2ff6e9c1043895b90674587ba5c6fbe5e6616ce Mon Sep 17 00:00:00 2001 From: Mikiyas-STP Date: Thu, 16 Oct 2025 19:43:19 +0100 Subject: [PATCH 05/13] methods --- prep_exercises/exercise3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prep_exercises/exercise3.py b/prep_exercises/exercise3.py index e49f760..7c2ac59 100644 --- a/prep_exercises/exercise3.py +++ b/prep_exercises/exercise3.py @@ -26,4 +26,4 @@ def get_city(person: Person) -> str: return person.city #person has no attribute city. -#to run mypy mypy ./***.py +#to run mypy mypy ./***.py \ No newline at end of file From 959803fcb73daf43f4fc64efe6b47795c4de8d8e Mon Sep 17 00:00:00 2001 From: Mikiyas-STP Date: Thu, 16 Oct 2025 19:49:00 +0100 Subject: [PATCH 06/13] =?UTF-8?q?exercise5=20using=20@dataclass.=20=20=20D?= =?UTF-8?q?ataclasses=20simplify=20the=20creation=20of=20=E2=80=9Cvalue=20?= =?UTF-8?q?objects=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prep_exercises/exercise5.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 prep_exercises/exercise5.py diff --git a/prep_exercises/exercise5.py b/prep_exercises/exercise5.py new file mode 100644 index 0000000..ec692d8 --- /dev/null +++ b/prep_exercises/exercise5.py @@ -0,0 +1,22 @@ +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: + 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): #if bd havent occured even once this year -1 + age -= 1 + return age >= 18 + +#example +imran = Person("Imran", date(2003, 7, 14), "Ubuntu") +eliza = Person("Eliza", date(1991, 3, 25), "Arch Linux") +print(imran) +print(imran.is_adult()) +print(eliza.is_adult()) From c1fa81aabc01b2255fd5096aa4212e4c00f06b78 Mon Sep 17 00:00:00 2001 From: Mikiyas-STP Date: Thu, 16 Oct 2025 19:52:00 +0100 Subject: [PATCH 07/13] the exercise --- prep_exercises/exercise6.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 prep_exercises/exercise6.py diff --git a/prep_exercises/exercise6.py b/prep_exercises/exercise6.py new file mode 100644 index 0000000..9c90e6e --- /dev/null +++ b/prep_exercises/exercise6.py @@ -0,0 +1,19 @@ +from dataclasses import dataclass +from typing import List + +@dataclass(frozen=True) +class Person: + name: str + children: List["Person"] + +fatma = Person(name="Fatma", children=[]) +aisha = Person(name="Aisha", children=[]) + +imran = Person(name="Imran", 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) \ No newline at end of file From 037482f2e14387a2a11dd64932d885ee0abdf170 Mon Sep 17 00:00:00 2001 From: Mikiyas-STP Date: Thu, 16 Oct 2025 19:56:41 +0100 Subject: [PATCH 08/13] using generic, frozen with data class --- prep_exercises/exercise6.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/prep_exercises/exercise6.py b/prep_exercises/exercise6.py index 9c90e6e..8ca96da 100644 --- a/prep_exercises/exercise6.py +++ b/prep_exercises/exercise6.py @@ -4,16 +4,19 @@ @dataclass(frozen=True) class Person: name: str + age: int children: List["Person"] -fatma = Person(name="Fatma", children=[]) -aisha = Person(name="Aisha", children=[]) +fatma = Person(name="Fatma", age=8, children=[]) +aisha = Person(name="Aisha", age=6, children=[]) + +imran = Person(name="Imran", age=35, children=[fatma, aisha]) -imran = Person(name="Imran", 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) \ No newline at end of file From 2cac78b7718babc91704d9295fbfb4cbbc7bc1ef Mon Sep 17 00:00:00 2001 From: Mikiyas-STP Date: Thu, 16 Oct 2025 19:58:10 +0100 Subject: [PATCH 09/13] nonrefactored version of the exercise --- prep_exercises/exercise7.py | 42 +++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 prep_exercises/exercise7.py diff --git a/prep_exercises/exercise7.py b/prep_exercises/exercise7.py new file mode 100644 index 0000000..627e1be --- /dev/null +++ b/prep_exercises/exercise7.py @@ -0,0 +1,42 @@ +from dataclasses import dataclass +from typing import List + +@dataclass(frozen=True) +class Person: + name: str + age: int + preferred_operating_system: 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 == person.preferred_operating_system: + possible_laptops.append(laptop) + return possible_laptops + + +people = [ + Person(name="Imran", age=22, preferred_operating_system="Ubuntu"), + Person(name="Eliza", age=34, preferred_operating_system="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}") \ No newline at end of file From f6e0e5780e13739703542fe11a4a5bb7753cfe29 Mon Sep 17 00:00:00 2001 From: Mikiyas-STP Date: Thu, 16 Oct 2025 20:01:14 +0100 Subject: [PATCH 10/13] refactored and fixes on types --- prep_exercises/exercise7.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/prep_exercises/exercise7.py b/prep_exercises/exercise7.py index 627e1be..a0560a6 100644 --- a/prep_exercises/exercise7.py +++ b/prep_exercises/exercise7.py @@ -1,12 +1,10 @@ from dataclasses import dataclass from typing import List - @dataclass(frozen=True) class Person: name: str age: int - preferred_operating_system: str - + preferred_operating_systems: List[str] @dataclass(frozen=True) class Laptop: @@ -18,16 +16,17 @@ class Laptop: def find_possible_laptops(laptops: List[Laptop], person: Person) -> List[Laptop]: - possible_laptops = [] + # create a set of lowercase preferred names for faster membership checks + prefs_lower = {os.lower() for os in person.preferred_operating_systems} + possible_laptops: List[Laptop] = [] for laptop in laptops: - if laptop.operating_system == person.preferred_operating_system: + if laptop.operating_system.lower() in prefs_lower: possible_laptops.append(laptop) return possible_laptops - people = [ - Person(name="Imran", age=22, preferred_operating_system="Ubuntu"), - Person(name="Eliza", age=34, preferred_operating_system="Arch Linux"), + Person(name="Imran", age=22, preferred_operating_systems=["Ubuntu", "Arch Linux"]), + Person(name="Eliza", age=34, preferred_operating_systems=["Arch Linux"]), ] laptops = [ From 0fc053a2630681348a8b591a01a568df1768d678 Mon Sep 17 00:00:00 2001 From: Mikiyas-STP Date: Thu, 16 Oct 2025 20:05:10 +0100 Subject: [PATCH 11/13] matching preferences --- prep_exercises/exercise7.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/prep_exercises/exercise7.py b/prep_exercises/exercise7.py index a0560a6..511a90d 100644 --- a/prep_exercises/exercise7.py +++ b/prep_exercises/exercise7.py @@ -17,7 +17,7 @@ class Laptop: def find_possible_laptops(laptops: List[Laptop], person: Person) -> List[Laptop]: # create a set of lowercase preferred names for faster membership checks - prefs_lower = {os.lower() for os in person.preferred_operating_systems} + prefs_lower = {os.lower() for os in person.preferred_operating_systems} #turn the string in to lowercase and it stores is on our set called prefs_lower possible_laptops: List[Laptop] = [] for laptop in laptops: if laptop.operating_system.lower() in prefs_lower: @@ -38,4 +38,6 @@ def find_possible_laptops(laptops: List[Laptop], person: Person) -> List[Laptop] for person in people: possible_laptops = find_possible_laptops(laptops, person) - print(f"Possible laptops for {person.name}: {possible_laptops}") \ No newline at end of file + print(f"Possible laptops for {person.name}: {possible_laptops}") +# now ubuntu and Ubuntu is the same match +# by basically checking if the lower case version of our text is there we declare it is the same thing \ No newline at end of file From 9b4bfadfbcb1cc8f1ce6f83aba26198bc7f4e825 Mon Sep 17 00:00:00 2001 From: Mikiyas-STP Date: Thu, 16 Oct 2025 20:12:26 +0100 Subject: [PATCH 12/13] enums and type safe data classes --- prep_exercises/exercise8laptopallocation.py | 61 +++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 prep_exercises/exercise8laptopallocation.py diff --git a/prep_exercises/exercise8laptopallocation.py b/prep_exercises/exercise8laptopallocation.py new file mode 100644 index 0000000..ca5dfd6 --- /dev/null +++ b/prep_exercises/exercise8laptopallocation.py @@ -0,0 +1,61 @@ +from dataclasses import dataclass +from enum import Enum +from typing import List +from collections import Counter +import sys + +class OperatingSystem(Enum): + MACOS = "macOS" + ARCH = "Arch Linux" + UBUNTU = "Ubuntu" + + +@dataclass(frozen=True) +class Laptop: + id: int + manufacturer: str + model: str + screen_size_in_inches: float + operating_system: OperatingSystem + +@dataclass(frozen=True) +class Person: + name: str + age: int + preferred_operating_system: OperatingSystem + +#library’s laptops +laptops = [ + Laptop(1, "Dell", "XPS 13", 13, OperatingSystem.ARCH), + Laptop(2, "Dell", "XPS 15", 15, OperatingSystem.UBUNTU), + Laptop(3, "Lenovo", "ThinkPad", 14, OperatingSystem.UBUNTU), + Laptop(4, "Apple", "MacBook Air", 13, OperatingSystem.MACOS), +] +#user input +name = input("Enter your name: ").strip() +try: + age = int(input("Enter your age: ").strip()) +except ValueError: + print("Error Age must be a number ", file=sys.stderr) + sys.exit(1) +os_input = input("Enter your preferred operating system (macOS / Arch Linux / Ubuntu): ").strip() +try: + preferred_os = OperatingSystem(os_input) +except ValueError: + print(f"Error '{os_input}' is not a valid operating system.", file=sys.stderr) + print("Valid options are:", [os.value for os in OperatingSystem], file=sys.stderr) + sys.exit(1) + +#Processing +person = Person(name=name, age=age, preferred_operating_system=preferred_os) +matching_laptops = [l for l in laptops if l.operating_system == person.preferred_operating_system] + +print(f"\n library has {len(matching_laptops)} laptop/s with {preferred_os.value}") + +#suggest an alternative if better availability +os_counts = Counter([l.operating_system for l in laptops]) +most_common_os, most_common_count = os_counts.most_common(1)[0] + +if most_common_os != preferred_os and most_common_count > len(matching_laptops): + print(f"💡 There are more laptops with {most_common_os.value}.") + print(f"If you’re willing to use {most_common_os.value}, you’re more likely to get one.") From f113cf17bdfa73fd599e21431056511433206d7c Mon Sep 17 00:00:00 2001 From: Mikiyas-STP Date: Thu, 16 Oct 2025 20:19:57 +0100 Subject: [PATCH 13/13] last exeercise --- prep_exercises/exercise9inheritance.py | 49 ++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 prep_exercises/exercise9inheritance.py diff --git a/prep_exercises/exercise9inheritance.py b/prep_exercises/exercise9inheritance.py new file mode 100644 index 0000000..8d8866d --- /dev/null +++ b/prep_exercises/exercise9inheritance.py @@ -0,0 +1,49 @@ +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()) +print(person1.get_full_name()) +person1.change_last_name("Tyurina") +print(person1.get_name()) +print(person1.get_full_name()) + +person2 = Parent("Elizaveta", "Alekseeva") +print(person2.get_name()) +#print(person2.get_full_name()) +#person2.change_last_name("Tyurina") +print(person2.get_name()) +#print(person2.get_full_name()) + + +#without commenting the three lines the output was like thiss +#Elizaveta Alekseeva +#Elizaveta Alekseeva +#Elizaveta Tyurina +#Elizaveta Alekseeva +#Elizaveta Tyurina (née Alekseeva) +#Traceback (most recent call last): +# File "/Users/Module-Decomposition/prep_exercises/exercise9inheritance.py", line 34, in +# print(person2.get_full_name()) +#AttributeError: 'Parent' object has no attribute 'get_full_name' \ No newline at end of file