From f400d7188adfbb62f0c1d6198dbe462b21f75bed Mon Sep 17 00:00:00 2001 From: ameneh-keshavarz Date: Sat, 21 Jun 2025 23:28:51 +0100 Subject: [PATCH 1/2] implement prep exercises --- dataclass.py | 19 ++++++++++++++ enums.py | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++ generic.py | 20 +++++++++++++++ inheritance.py | 37 ++++++++++++++++++++++++++ method.py | 18 +++++++++++++ objectclass.py | 11 ++++++++ refactoring.py | 42 ++++++++++++++++++++++++++++++ typecheck.py | 32 +++++++++++++++++++++++ 8 files changed, 249 insertions(+) create mode 100644 dataclass.py create mode 100644 enums.py create mode 100644 generic.py create mode 100644 inheritance.py create mode 100644 method.py create mode 100644 objectclass.py create mode 100644 refactoring.py create mode 100644 typecheck.py diff --git a/dataclass.py b/dataclass.py new file mode 100644 index 00000000..f835bed3 --- /dev/null +++ b/dataclass.py @@ -0,0 +1,19 @@ +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): + age -= 1 + return age >= 18 + +imran = Person("Imran", date(2010, 6, 10), "Ubuntu") +print(imran) +print(imran.is_adult()) diff --git a/enums.py b/enums.py new file mode 100644 index 00000000..7cf3dd82 --- /dev/null +++ b/enums.py @@ -0,0 +1,70 @@ +from dataclasses import dataclass +from enum import Enum +from typing import List +import sys + +class OperatingSystem(Enum): + MACOS = "macOS" + ARCH = "Arch Linux" + UBUNTU = "Ubuntu" + + @staticmethod + def from_string(value: str): + for os in OperatingSystem: + if os.value.lower() == value.strip().lower(): + return os + raise ValueError(f"Sorry, we don't support that operating system: {value}") + +@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]: + return [laptop for laptop in laptops if laptop.operating_system == person.preferred_operating_system] + +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), + Laptop(id=5, manufacturer="Apple", model="macBook Pro", screen_size_in_inches=15, operating_system=OperatingSystem.MACOS), +] + +try: + name = input("What's your name? ").strip() + + age_input = input("How old are you? ").strip() + if not age_input.isdigit(): + raise ValueError("Age must be a number.") + age = int(age_input) + + os_input = input("Which operating system do you like? (Ubuntu, Arch Linux, macOS): ").strip() + preferred_os = OperatingSystem.from_string(os_input) + + person = Person(name=name, age=age, preferred_operating_system=preferred_os) + +except ValueError as e: + print(f"{e}", file=sys.stderr) + sys.exit(1) + +matching = find_possible_laptops(laptops, person) +print(f"\nHi {person.name}, we have {len(matching)} laptop(s) with {preferred_os.value}.") + +os_counts = {os: 0 for os in OperatingSystem} +for laptop in laptops: + os_counts[laptop.operating_system] += 1 + +most_available_os = max(os_counts, key=os_counts.get) +if most_available_os != preferred_os and os_counts[most_available_os] > len(matching): + print(f"We have more laptops with {most_available_os.value}.") + print(f"If you're okay with using {most_available_os.value}, you might get one faster.") diff --git a/generic.py b/generic.py new file mode 100644 index 00000000..b59bdb82 --- /dev/null +++ b/generic.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=5, children=[]) +aisha = Person(name="Aisha", age=7, children=[]) + +imran = Person(name="Imran", age=35, 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) diff --git a/inheritance.py b/inheritance.py new file mode 100644 index 00000000..45bfbb32 --- /dev/null +++ b/inheritance.py @@ -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()) +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()) # error: Parent doesn't have get_full_name() +person2.change_last_name("Tyurina") # error: Parent doesn't have change_last_name() +print(person2.get_name()) +print(person2.get_full_name()) # error: Parent doesn't have get_full_name() diff --git a/method.py b/method.py new file mode 100644 index 00000000..c6ad7322 --- /dev/null +++ b/method.py @@ -0,0 +1,18 @@ +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 + +imran = Person("Imran", date(2010, 6, 10), "Ubuntu") +print(imran.is_adult()) diff --git a/objectclass.py b/objectclass.py new file mode 100644 index 00000000..860858b4 --- /dev/null +++ b/objectclass.py @@ -0,0 +1,11 @@ +class Person: + def __init__(self, name: str, age: int): + self.name = name + self.age = age + +def is_adult(person: Person) -> bool: + return person.age >= 18 + +# This attribute does not exist so it causes error +def print_hair_color(person: Person) -> None: + print(person.hair_color) diff --git a/refactoring.py b/refactoring.py new file mode 100644 index 00000000..231cc95e --- /dev/null +++ b/refactoring.py @@ -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", "Arch Linux"]), + 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}") diff --git a/typecheck.py b/typecheck.py new file mode 100644 index 00000000..fb1ddd11 --- /dev/null +++ b/typecheck.py @@ -0,0 +1,32 @@ +from typing import Dict + +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 76f7c3de00e6d3716d027425cb79b1e647718782 Mon Sep 17 00:00:00 2001 From: ameneh-keshavarz Date: Mon, 23 Jun 2025 07:26:28 +0100 Subject: [PATCH 2/2] implement prep exercises --- .gitignore | 16 ++++++++++++++++ dataclass.py | 19 +++++++++++-------- enums.py | 15 ++++++++++++++- generic.py | 12 +++++++++--- inheritance.py | 30 +++++++++++++++++++----------- method.py | 14 ++++++++++---- objectclass.py | 11 +++++++---- 7 files changed, 86 insertions(+), 31 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..ee425719 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +.venv/ + + + + + + + + + + + + + + + diff --git a/dataclass.py b/dataclass.py index f835bed3..c55f83d9 100644 --- a/dataclass.py +++ b/dataclass.py @@ -1,19 +1,22 @@ +# This code defines a Person with a name, date of birth, and favorite operating system. +# It also checks if the person is 18 years old or older. + from dataclasses import dataclass from datetime import date -@dataclass(frozen=True) +@dataclass(frozen=True) # Makes the object read-only after creation 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 + today = date.today() # Get today's date + age = today.year - self.date_of_birth.year # Calculate age if (today.month, today.day) < (self.date_of_birth.month, self.date_of_birth.day): - age -= 1 - return age >= 18 + age -= 1 # Subtract 1 if birthday hasn't happened yet this year + return age >= 18 # True if age is 18 or more -imran = Person("Imran", date(2010, 6, 10), "Ubuntu") -print(imran) -print(imran.is_adult()) +imran = Person("Imran", date(2010, 6, 10), "Ubuntu") # Create a person +print(imran) # Show person info +print(imran.is_adult()) # Check if Imran is an adult \ No newline at end of file diff --git a/enums.py b/enums.py index 7cf3dd82..72c05482 100644 --- a/enums.py +++ b/enums.py @@ -1,8 +1,13 @@ +# This program asks the user for their name, age, and favorite operating system. +# It then shows laptops that match the person's OS preference. +# If another OS has more laptops available, it gives a suggestion. + from dataclasses import dataclass from enum import Enum from typing import List import sys +# Define supported operating systems class OperatingSystem(Enum): MACOS = "macOS" ARCH = "Arch Linux" @@ -15,12 +20,14 @@ def from_string(value: str): return os raise ValueError(f"Sorry, we don't support that operating system: {value}") +# Person has a name, age, and favorite operating system @dataclass(frozen=True) class Person: name: str age: int preferred_operating_system: OperatingSystem +# Laptop has details including operating system @dataclass(frozen=True) class Laptop: id: int @@ -29,9 +36,11 @@ class Laptop: screen_size_in_inches: float operating_system: OperatingSystem +# Find laptops that match the person's preferred OS def find_possible_laptops(laptops: List[Laptop], person: Person) -> List[Laptop]: return [laptop for laptop in laptops if laptop.operating_system == person.preferred_operating_system] +# Create a list of available laptops 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), @@ -51,19 +60,23 @@ def find_possible_laptops(laptops: List[Laptop], person: Person) -> List[Laptop] os_input = input("Which operating system do you like? (Ubuntu, Arch Linux, macOS): ").strip() preferred_os = OperatingSystem.from_string(os_input) + # Create the person object from user input person = Person(name=name, age=age, preferred_operating_system=preferred_os) except ValueError as e: print(f"{e}", file=sys.stderr) - sys.exit(1) + sys.exit(1) # Exit if input is not valid +# Find matching laptops matching = find_possible_laptops(laptops, person) print(f"\nHi {person.name}, we have {len(matching)} laptop(s) with {preferred_os.value}.") +# Count how many laptops there are for each OS os_counts = {os: 0 for os in OperatingSystem} for laptop in laptops: os_counts[laptop.operating_system] += 1 +# Suggest other OS if more laptops are available with it most_available_os = max(os_counts, key=os_counts.get) if most_available_os != preferred_os and os_counts[most_available_os] > len(matching): print(f"We have more laptops with {most_available_os.value}.") diff --git a/generic.py b/generic.py index b59bdb82..d6561e2b 100644 --- a/generic.py +++ b/generic.py @@ -1,20 +1,26 @@ +# This program creates a person with children and prints a simple family tree. + from dataclasses import dataclass from typing import List +# Define a Person with a name, age, and a list of children (who are also Person objects) @dataclass(frozen=True) class Person: name: str age: int - children: List["Person"] + children: List["Person"] # List of other Person objects +# Create children fatma = Person(name="Fatma", age=5, children=[]) aisha = Person(name="Aisha", age=7, children=[]) +# Create parent with children imran = Person(name="Imran", age=35, children=[fatma, aisha]) +# Print person's name and their children def print_family_tree(person: Person) -> None: print(person.name) for child in person.children: - print(f"- {child.name} ({child.age})") + print(f"- {child.name} ({child.age})") # Print each child's name and age -print_family_tree(imran) +print_family_tree(imran) # Show family tree starting from Imran diff --git a/inheritance.py b/inheritance.py index 45bfbb32..0901662c 100644 --- a/inheritance.py +++ b/inheritance.py @@ -1,3 +1,5 @@ +# This program shows inheritance: a Child class that extends Parent and adds extra features. + class Parent: def __init__(self, first_name: str, last_name: str): self.first_name = first_name @@ -9,8 +11,8 @@ def get_name(self) -> str: class Child(Parent): def __init__(self, first_name: str, last_name: str): - super().__init__(first_name, last_name) - self.previous_last_names = [] + super().__init__(first_name, last_name) # Use Parent's init + self.previous_last_names = [] # Track old last names def change_last_name(self, last_name) -> None: self.previous_last_names.append(self.last_name) @@ -22,16 +24,22 @@ def get_full_name(self) -> str: suffix = f" (née {self.previous_last_names[0]})" return f"{self.first_name} {self.last_name}{suffix}" + +# Create a Child object and test name changes person1 = Child("Elizaveta", "Alekseeva") -print(person1.get_name()) -print(person1.get_full_name()) +print(person1.get_name()) # Elizaveta Alekseeva +print(person1.get_full_name()) # Elizaveta Alekseeva person1.change_last_name("Tyurina") -print(person1.get_name()) -print(person1.get_full_name()) +print(person1.get_name()) # Elizaveta Tyurina +print(person1.get_full_name()) # Elizaveta Tyurina (née Alekseeva) +# Create a Parent object person2 = Parent("Elizaveta", "Alekseeva") -print(person2.get_name()) -print(person2.get_full_name()) # error: Parent doesn't have get_full_name() -person2.change_last_name("Tyurina") # error: Parent doesn't have change_last_name() -print(person2.get_name()) -print(person2.get_full_name()) # error: Parent doesn't have get_full_name() +print(person2.get_name()) # Works: method exists + +# The following lines will cause errors because Parent doesn't have those methods +# print(person2.get_full_name()) # Error: Parent has no get_full_name() +# person2.change_last_name("Tyurina") # Error: Parent has no change_last_name() +# print(person2.get_full_name()) # Error again + +# To fix the errors above, you'd need to check the object type before calling those methods diff --git a/method.py b/method.py index c6ad7322..9112dd7c 100644 --- a/method.py +++ b/method.py @@ -1,3 +1,6 @@ +# This program defines a person with name, birthdate, and favorite OS, +# then checks if the person is an adult (18 or older). + from datetime import date class Person: @@ -7,12 +10,15 @@ def __init__(self, name: str, date_of_birth: date, preferred_operating_system: s self.preferred_operating_system = preferred_operating_system def is_adult(self) -> bool: - today = date.today() - age = today.year - self.date_of_birth.year + today = date.today() # Get today's date + age = today.year - self.date_of_birth.year # Find age by year difference + + # If birthday hasn't come yet this year, subtract 1 from age if (today.month, today.day) < (self.date_of_birth.month, self.date_of_birth.day): age -= 1 - return age >= 18 + return age >= 18 # Return True if 18 or older +# Create a person named Imran with birthday June 10, 2010 imran = Person("Imran", date(2010, 6, 10), "Ubuntu") -print(imran.is_adult()) +print(imran.is_adult()) # Check if Imran is an adult (True/False) diff --git a/objectclass.py b/objectclass.py index 860858b4..8c5f2a9a 100644 --- a/objectclass.py +++ b/objectclass.py @@ -1,11 +1,14 @@ +# This program defines a Person with name and age, +# and includes a function to check if they are an adult. + class Person: def __init__(self, name: str, age: int): self.name = name - self.age = age + self.age = age # hair_color is NOT defined here def is_adult(person: Person) -> bool: - return person.age >= 18 + return person.age >= 18 # Returns True if age is 18 or more -# This attribute does not exist so it causes error +# This function will cause an error because 'hair_color' doesn't exist def print_hair_color(person: Person) -> None: - print(person.hair_color) + print(person.hair_color) # AttributeError if hair_color not added