-
-
Notifications
You must be signed in to change notification settings - Fork 105
London | 26-Jul-SDC | Roman Sanaye | Sprint 5 | Prep Exercises #650
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| 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.age) | ||
|
|
||
| eliza = Person("Eliza", 34, "Arch Linux") | ||
| print(eliza.name) | ||
| print(eliza.age) | ||
|
|
||
|
|
||
| def is_adult(person: Person) -> bool: | ||
| return person.age >= 18 | ||
|
|
||
|
|
||
| print(is_adult(imran)) | ||
|
|
||
|
|
||
| def get_address(person: Person) -> str: | ||
| return person.address | ||
| # it still gives error as Person class does not have any attribute called address. | ||
|
|
||
|
|
||
| # this is the error given by mypy: | ||
| # class-in-python.py:10: error: "Person" has no attribute "address" [attr-defined] | ||
| # class-in-python.py:14: error: "Person" has no attribute "address" [attr-defined] | ||
| # Found 2 errors in 1 file (checked 1 source file) | ||
|
|
||
| # Solution: | ||
| # The Person class doesn't define an address attribute, but the code tries to access it. To fix the error, either remove the address references or define address as an attribute in the constructor and provide it when creating each Person object. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| from datetime import date | ||
| from dataclasses import dataclass | ||
|
|
||
|
|
||
| @dataclass | ||
| 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 | ||
|
|
||
| return age >= 18 | ||
|
|
||
|
|
||
| imran = Person("Imran", date(2004, 10, 10), "Ubuntu") | ||
| print(imran.is_adult()) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| from enum import Enum | ||
| from dataclasses import dataclass | ||
| import sys | ||
|
|
||
|
|
||
| # Enum gives us a fixed set of operating system choices. | ||
| class OperatingSystem(Enum): | ||
| MACOS = "macOS" | ||
| ARCH = "Arch Linux" | ||
| UBUNTU = "Ubuntu" | ||
|
|
||
|
|
||
| # Dataclass automatically creates the __init__ method for us. | ||
| @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 | ||
|
|
||
|
|
||
| # The laptops already available in the library. | ||
| 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, | ||
| ), | ||
| ] | ||
|
|
||
|
|
||
| # Get the user's name. | ||
| name = input("What is your name? ") | ||
|
|
||
|
|
||
| # Convert the age from a string to an integer. | ||
| # If conversion fails, print the error to stderr and exit with code 1. | ||
| try: | ||
| age = int(input("What is your age? ")) | ||
| except ValueError: | ||
| print("Invalid age.", file=sys.stderr) | ||
| sys.exit(1) | ||
|
|
||
|
|
||
| # Convert the user's input into an OperatingSystem enum value. | ||
| # If the value isn't one of our enum choices, exit with an error. | ||
| try: | ||
| preferred_operating_system = OperatingSystem( | ||
| input("What is your preferred operating system? ") | ||
| ) | ||
| except ValueError: | ||
| print("Invalid operating system.", file=sys.stderr) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you think of a more graceful UX for handling invalid inputs? |
||
| sys.exit(1) | ||
|
|
||
|
|
||
| # Create a Person using the validated input. | ||
| person = Person( | ||
| name=name, | ||
| age=age, | ||
| preferred_operating_system=preferred_operating_system, | ||
| ) | ||
|
|
||
|
|
||
| # Count laptops matching the person's preferred operating system. | ||
| count = 0 | ||
|
|
||
| for laptop in laptops: | ||
| if laptop.operating_system == person.preferred_operating_system: | ||
| count += 1 | ||
|
|
||
|
|
||
| print( | ||
| f"There are {count} laptops available with " | ||
| f"{person.preferred_operating_system.value}." | ||
| ) | ||
|
|
||
|
|
||
| # Count how many laptops are available for each operating system. | ||
| available_laptops = {} | ||
|
|
||
| for laptop in laptops: | ||
| os = laptop.operating_system | ||
|
|
||
| if os not in available_laptops: | ||
| available_laptops[os] = 0 | ||
|
|
||
| available_laptops[os] += 1 | ||
|
|
||
|
|
||
| # Find the operating system with the most available laptops. | ||
| most_available_os = max( | ||
| available_laptops, | ||
| key=available_laptops.__getitem__, | ||
| ) | ||
|
|
||
|
|
||
| # If another operating system has more laptops, recommend it. | ||
| if most_available_os != person.preferred_operating_system: | ||
| print( | ||
| f"You are more likely to get a laptop if you accept " | ||
| f"{most_available_os.value}." | ||
| ) | ||
|
|
||
|
|
||
| # # LAPTOP LIBRARY PROGRAM FLOW: | ||
| # | ||
| # 1. Define OperatingSystem enum | ||
| # ↓ | ||
| # 2. Define Person and Laptop dataclasses | ||
| # ↓ | ||
| # 3. Create list of available laptops | ||
| # ↓ | ||
| # 4. Get user's name, age, and preferred OS | ||
| # ↓ | ||
| # 5. Validate and convert user input | ||
| # ↓ | ||
| # 6. Create a Person object | ||
| # ↓ | ||
| # 7. Count laptops matching the user's preferred OS | ||
| # ↓ | ||
| # 8. Count laptops for each operating system | ||
| # ↓ | ||
| # 9. Find the OS with the most laptops | ||
| # ↓ | ||
| # 10. Compare it with the user's preferred OS | ||
| # ↓ | ||
| # 11. Recommend another OS if more laptops are available | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| from dataclasses import dataclass | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class Person: | ||
| name: str | ||
| age: int | ||
| children: list | ||
|
|
||
|
|
||
| fatma = Person(name="Fatma", age= 22, children=[]) | ||
| aisha = Person(name="Aisha", age = 15, children=[]) | ||
|
|
||
| imran = Person(name="Imran", age = 45, 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) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| from typing import Iterable, Optional | ||
|
|
||
|
|
||
| class ImmutableNumberList: | ||
| # We accept any `Iterable[int]` here, so can construct with a list, a set, or anything else that can be iterated. | ||
| def __init__(self, elements: Iterable[int]): | ||
| # We copy the elements so that if someone mutates the passed in elements list, our copy won't be mutated. | ||
| self.elements = [element for element in elements] | ||
|
|
||
| def first(self) -> Optional[int]: | ||
| if not self.elements: | ||
| return None | ||
| return self.elements[0] | ||
|
|
||
| def last(self) -> Optional[int]: | ||
| if not self.elements: | ||
| return None | ||
| return self.elements[-1] | ||
|
|
||
| def length(self) -> int: | ||
| return len(self.elements) | ||
|
|
||
| def largest(self) -> Optional[int]: | ||
| # To find the largest element, we need to go through the entire list (which may take some time). | ||
| if not self.elements: | ||
| return None | ||
| largest = self.elements[0] | ||
| for element in self.elements: | ||
| if element > largest: | ||
| largest = element | ||
| return largest | ||
|
|
||
|
|
||
| # A SortedImmutableNumberList is the same as an ImmutableNumberList, | ||
| # but it changes some aspects. | ||
| class SortedImmutableNumberList(ImmutableNumberList): | ||
| def __init__(self, elements: Iterable[int]): | ||
| # We do extra work here when constructing the list, | ||
| # to make sure the elements are sorted. | ||
| # This takes more time than the ImmutableNumberList version would. | ||
| super().__init__(sorted(elements)) | ||
|
|
||
| # This method overrides (replaces) the method with the same name on the super-class. | ||
| def largest(self) -> Optional[int]: | ||
| # Because we know the elements were already sorted in the constructor, | ||
| # we can implement finding the largest number faster. | ||
| # We don't need to look through every element - we know the largest element is at the end. | ||
| # Because we did extra work one time before (in the constructor), | ||
| # we can avoid re-doing that work every time someone calls `largest()`. | ||
| return self.last() | ||
|
|
||
| def max_gap_between_values(self) -> Optional[int]: | ||
| if not self.elements: | ||
| return None | ||
| previous_element = None | ||
| max_gap = -1 | ||
| for element in self.elements: | ||
| if previous_element is not None: | ||
| gap = element - previous_element | ||
| if gap > max_gap: | ||
| max_gap = gap | ||
| previous_element = element | ||
| return max_gap | ||
|
|
||
|
|
||
| values = SortedImmutableNumberList([1, 19, 7, 13, 4]) | ||
| print(values.largest()) | ||
| print(values.max_gap_between_values()) | ||
|
|
||
| unsorted_values = ImmutableNumberList([1, 19, 7, 13, 4]) | ||
| print(unsorted_values.largest()) | ||
| # print(unsorted_values.max_gap_between_values()) |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good explanations |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| Advantages of methods: | ||
|
|
||
| They keep related code together. A method belongs to a class, so it keeps the behavior related to that object in one place. | ||
|
|
||
| They are easier to understand. When you see person.is_adult(), it is clear that the action is related to a Person. | ||
|
|
||
| They can directly access the object's data. A method can use self to access attributes of the object. | ||
|
|
||
| They make code more organized. Classes group the data and the operations that work with that data together. | ||
|
|
||
| They can make code easier to reuse. Once a class has a method, every instance of that class can use it. | ||
|
|
||
| Encapsulation - if we change the implementation of Person (e.g. we start storing a date of birth instead of an age), it’s more obvious what things we need to change. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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): | ||
| today = date.today() | ||
| age = today.year - self.date_of_birth.year | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will this definitely give the correct age for all possible days? |
||
|
|
||
| return age >= 18 | ||
|
|
||
|
|
||
| imran = Person("Imran", date(2004, 10, 10), "Ubuntu") | ||
| print(imran.is_adult()) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean to commit this file? How could you prevent it from being comitted in future?