From 4cd664e3906ae2dbf0e974848c01bc1f39ccc49b Mon Sep 17 00:00:00 2001 From: Abubakar-Meigag <113248042+Abubakar-Meigag@users.noreply.github.com> Date: Fri, 22 May 2026 19:14:31 +0100 Subject: [PATCH] London | 2026-mar-sdc | beko | Sprint 5 | prep exercises complete sprint 5 exercises --- prep exercises/checking_mypy.py | 32 ++++++++ prep exercises/dataclass.py | 26 +++++++ prep exercises/double.py | 18 +++++ prep exercises/enums.py | 126 ++++++++++++++++++++++++++++++++ prep exercises/generics.py | 20 +++++ prep exercises/inheritance.py | 72 ++++++++++++++++++ prep exercises/methods.py | 42 +++++++++++ prep exercises/person_class.py | 29 ++++++++ prep exercises/refactor.py | 44 +++++++++++ 9 files changed, 409 insertions(+) create mode 100644 prep exercises/checking_mypy.py create mode 100644 prep exercises/dataclass.py create mode 100644 prep exercises/double.py create mode 100644 prep exercises/enums.py create mode 100644 prep exercises/generics.py create mode 100644 prep exercises/inheritance.py create mode 100644 prep exercises/methods.py create mode 100644 prep exercises/person_class.py create mode 100644 prep exercises/refactor.py diff --git a/prep exercises/checking_mypy.py b/prep exercises/checking_mypy.py new file mode 100644 index 00000000..4cd58d49 --- /dev/null +++ b/prep exercises/checking_mypy.py @@ -0,0 +1,32 @@ + + +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, +} + +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}") \ No newline at end of file diff --git a/prep exercises/dataclass.py b/prep exercises/dataclass.py new file mode 100644 index 00000000..5981c40d --- /dev/null +++ b/prep exercises/dataclass.py @@ -0,0 +1,26 @@ + + +from dataclasses import dataclass +from datetime import date + +@dataclass(frozen=True) +class Person: + name: str + birth_day: date + preferred_OS: str + + + def is_adult(self): + today = date.today() + age = today.year - self.birth_day.year + + if (today.month, today.day) < (self.birth_day.month, self.birth_day.day): + age -= 1 + return age >= 18 + + +beko = Person("Beko", date(2005, 2, 17), "Linux") +print(beko.is_adult()) + +noor = Person("Noor", date(2015, 2, 17), "Linux") +print(noor.is_adult()) \ No newline at end of file diff --git a/prep exercises/double.py b/prep exercises/double.py new file mode 100644 index 00000000..0a10abca --- /dev/null +++ b/prep exercises/double.py @@ -0,0 +1,18 @@ + +def double (num): + return num * 2 + +print(double("22")) + +# Predication +# Did it do what you expected? +# No, it didn't +# +# Why did it return the value it did? +# because there is a mistype in the function, it should take a number but the argument is string +# + +def double_the_number(num): + return num * 3 + +print(double_the_number(5)) \ No newline at end of file diff --git a/prep exercises/enums.py b/prep exercises/enums.py new file mode 100644 index 00000000..962457dc --- /dev/null +++ b/prep exercises/enums.py @@ -0,0 +1,126 @@ +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 + + +# Existing inventory list +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, + ), +] + + +def get_user_input() -> Person: + name = input("Enter your name: ").strip() + if not name: + print("Error: Name cannot be empty.", file=sys.stderr) + sys.exit(1) + + # 1. check the person's age is valid + try: + age = int(input("Enter your age: ")) + if age < 0: + raise ValueError + except ValueError: + print("Error: Age must be a positive whole number.", file=sys.stderr) + sys.exit(1) + + # 2. give the person a list of valid Operating Systems to choose from + print("\nAvailable Operating Systems:") + for os_option in OperatingSystem: + print(f"- {os_option.value}") + + os_input = input("Enter your preferred operating system: ").strip() + + # Try to match the user string to a valid Enum value + preferred_os = None + for os_option in OperatingSystem: + if os_option.value.lower() == os_input.lower(): + preferred_os = os_option + break + + if preferred_os is None: + print(f"Error: '{os_input}' is not a valid operating system.", file=sys.stderr) + sys.exit(1) + + return Person(name=name, age=age, preferred_operating_system=preferred_os) + + +def main(): + # Get valid user data + user = get_user_input() + + # Count laptops for each operating system + os_counts = {os_type: 0 for os_type in OperatingSystem} + for laptop in laptops: + os_counts[laptop.operating_system] += 1 + + # Look up counts for user choice and find the maximum available + user_os = user.preferred_operating_system + user_os_count = os_counts[user_os] + + most_available_os = max(os_counts, key=os_counts.get) + max_count = os_counts[most_available_os] + + # Output results + print(f"\nHello {user.name}!") + print(f"The library has {user_os_count} laptop(s) running {user_os.value}.") + + # Suggest alternative if another OS has more stock + if max_count > user_os_count: + print( + f"💡 Tip: If you choose {most_available_os.value}, we have {max_count} laptops available. You're more likely to get one quickly!" + ) + + +if __name__ == "__main__": + main() diff --git a/prep exercises/generics.py b/prep exercises/generics.py new file mode 100644 index 00000000..b672182d --- /dev/null +++ b/prep exercises/generics.py @@ -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=10, children=[]) +aisha = Person(name="Aisha", age=15, children=[]) + +imran = Person(name="Imran", age=40, 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 diff --git a/prep exercises/inheritance.py b/prep exercises/inheritance.py new file mode 100644 index 00000000..934a3562 --- /dev/null +++ b/prep exercises/inheritance.py @@ -0,0 +1,72 @@ + + +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") +# Predict: Creates a Child object. Works perfectly. + +print(person1.get_name()) +# Predict: it will print "Elizaveta Alekseeva" +# Child inherits this method from Parent + +print(person1.get_full_name()) +# Predict: it will print "Elizaveta Alekseeva" because the last name has not been changed yet. +# get_full_name is defined in Child, but it uses the last name from Parent, which is "Alekseeva" at this point. + +person1.change_last_name("Tyurina") +# Predict: Changes last_name to "Tyurina" and saves "Alekseeva" in the +# previous_last_names list. Works perfectly. + +print(person1.get_name()) +# Predict: Prints "Elizaveta Tyurina". +# get_name is inherited from Parent, but it uses the updated last name "Tyurina". + +print(person1.get_full_name()) +# Predict: Prints "Elizaveta Tyurina (née Alekseeva)". +# get_full_name uses the updated last name "Tyurina" and also includes the previous last name "Alekseeva" in the output. +print() + +person2 = Parent("Elizaveta", "Alekseeva") +# Predict: Creates a Parent object. Works perfectly. + +print(person2.get_name()) +# Predict: it will print "Elizaveta Alekseeva" +# get_name is defined in Parent, so it works perfectly. + +print(person2.get_full_name()) +# Predict: it will raise an AttributeError because get_full_name is not defined in Parent, +# and Parent does not have access to the methods of Child. + +person2.change_last_name("Tyurina") +# Predict: it will raise an AttributeError because change_last_name is not defined in Parent, +# and Parent does not have access to the methods of Child. + +print(person2.get_name()) +# Predict: it will print "Elizaveta Alekseeva" because the last name has not been changed due to the previous error. + +print(person2.get_full_name()) +# Predict: it will raise an AttributeError again for the same reason as before. diff --git a/prep exercises/methods.py b/prep exercises/methods.py new file mode 100644 index 00000000..9f1d3999 --- /dev/null +++ b/prep exercises/methods.py @@ -0,0 +1,42 @@ +from datetime import date + + +class Person: + def __init__(self, name: str, date_of_birth: date, preferred_operating_system: str, favorite_sport: str) -> None: + self.name = name + self.date_of_birth = date_of_birth + self.preferred_operating_system = preferred_operating_system + self.favorite_sport = favorite_sport + + def is_adult(self): + today = date.today() + age = today.year - self.date_of_birth.year + + birthday_passed = ( + (today.month, today.day) >= (self.date_of_birth.month, self.date_of_birth.day) + ) + if not birthday_passed: + age -= 1 + return age >= 18 + + + +beko = Person("Beko", date(2005, 2, 17), "Linux", "Football") +print(beko.is_adult()) + + +# Advantages of methods over free functions: + +# Keep data and behavior together: +# A method belongs directly to an object. It combines the object's info (data) and actions (behavior) into one package + +# Make code easier to read: +# You call them using object.action(). This reads like a normal sentence, making the code self-explanatory. +# same (imran.is_adult() is easier to understand than is_adult(imran)) + +# Support inheritance: +# Child objects automatically get access to the parent's methods. This means you do not have to copy and paste code. + +# Enable polymorphism: +# Different objects can use the exact same method name but handle it in their own unique way +# (like a Dog and a Cat both responding to a .makeSound() method) diff --git a/prep exercises/person_class.py b/prep exercises/person_class.py new file mode 100644 index 00000000..2bb7d40a --- /dev/null +++ b/prep exercises/person_class.py @@ -0,0 +1,29 @@ + + + +class Person: + def __init__(self, name: str, age: int, preferred_operating_system: str, favorite_sport: str) -> None: + self.name = name + self.age = age + self.preferred_operating_system = preferred_operating_system + self.favorite_sport = favorite_sport + + def is_adult(self) -> bool: + return self.age >= 18 + + def get_favorite_sport(self) -> str: + return self.favorite_sport + + + +imran = Person("Imran", 22, "Ubuntu", "Football") +print(imran.name) +print(imran.is_adult()) +print(imran.preferred_operating_system) +print(imran.get_favorite_sport()) + +eliza = Person("Eliza", 34, "Arch Linux", "Tennis") +print(eliza.name) +print(eliza.is_adult()) +print(eliza.preferred_operating_system) +print(eliza.get_favorite_sport()) \ No newline at end of file diff --git a/prep exercises/refactor.py b/prep exercises/refactor.py new file mode 100644 index 00000000..29a846e2 --- /dev/null +++ b/prep exercises/refactor.py @@ -0,0 +1,44 @@ +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"), + Person(name="Alice", age=28, preferred_operating_system="macOS"), + Person(name="Beko", age=30, preferred_operating_system="Red Hat Linux"), +] + +laptops = [ + Laptop(1, "Dell", "XPS", 13, "Arch Linux"), + Laptop(2, "Dell", "XPS", 15, "Ubuntu"), + Laptop(3, "Dell", "XPS", 15, "Red Hat Linux"), + Laptop(4, "Apple", "macBook", 13, "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