-
-
Notifications
You must be signed in to change notification settings - Fork 109
London | 26-Jul-SDC | Alex Jamshidi | Sprint 5 | Prep Exercises #680
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
78bdfd5
60b323e
3a35a7a
085224e
b28f41d
7d6a298
fce72f0
14d4d33
2b981ed
fa7436b
d4720b1
5c64429
769706f
65e60ae
efdd20b
61a6eaa
eeb4d7a
5455734
a0572e4
0cb7c2e
696e335
68d08db
27ffc84
0e0f38b
6b73659
831dda0
d10f140
fac816e
232223e
fd78436
d441881
d7d72c1
8922e28
87f4a2e
41a37e0
4fc4cca
c42b329
ae107b1
6325890
978812d
74fada8
de53660
e745980
edeea41
9fe6f6a
628383c
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 @@ | ||
| .venv |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # Exercise | ||
| # Predict what double("22") will do. | ||
| # It will treat "22" as a integer and return 11 as an integer | ||
|
|
||
| # Then run the code and check. | ||
| def double(value): | ||
| return value * 2 | ||
|
|
||
| print(double("22")) | ||
|
|
||
| # Did it do what you expected? Why did it return the value it did? | ||
| # No, it treated it like a string, so instead of trying to do math, it did string repetition. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| def double(number): | ||
| return number * 3 | ||
|
|
||
| print(double(10)) | ||
|
|
||
| # Read the above code and write down what the bug is. How would you fix it? | ||
|
|
||
| # Potential bug: Code says double and it is multiplying by 3 | ||
|
|
||
| # It could be fixed by changing the name of the function to triple, or the integer to 2. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| 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 = { | ||
| "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}") |
| 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.address) | ||
|
|
||
| eliza = Person("Eliza", 34, "Arch Linux") | ||
| print(eliza.name) | ||
| print(eliza.address) | ||
|
|
||
| # exercise_4_and_5.py:9: error: "Person" has no attribute "address" [attr-defined] | ||
| # exercise_4_and_5.py:13: error: "Person" has no attribute "address" [attr-defined] | ||
| # Found 2 errors in 1 file (checked 1 source file) | ||
|
|
||
| # There is no address attribute for a Person, only name, age and preferred operating system. | ||
|
|
||
| # Add the is_adult code to the file you saved earlier. | ||
| def is_adult(person: Person) -> bool: | ||
| return person.age >= 18 | ||
|
|
||
| print(is_adult(imran)) | ||
|
|
||
| # Run it through mypy - notice that no errors are reported - | ||
| # mypy understands that Person has a property named age so is happy with the function. | ||
|
|
||
| def is_banana(person: Person) -> bool: | ||
| return person.banana | ||
|
|
||
| print(is_banana(imran)) | ||
|
|
||
| # exercise_4_and_5.py:31: error: "Person" has no attribute "banana" [attr-defined] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # Think of the advantages of using methods instead of free functions. Write them down in your notebook. | ||
|
|
||
| # Better readability of code | ||
| # Encapsulation of that function only to class - cleaner and better security. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| from datetime import date | ||
|
|
||
| class Person: | ||
| def __init__(self, name: str, dob: date, preferred_operating_system: str): | ||
| self.name = name | ||
| self.dob = dob | ||
| self.preferred_operating_system = preferred_operating_system | ||
|
|
||
| def is_adult(self): | ||
| dob = self.dob | ||
| today = date.today() | ||
| if dob.year > today.year - 18: return False | ||
|
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. This sequence of if conditions feels a little complicated. Is there anything you could change to improve it / make it more readable / simplify it? |
||
| if dob.year == today.year - 18: | ||
| if dob.month > today.month: return False | ||
| if dob.month == today.month: | ||
| if dob.day > today.day: return False | ||
| return True | ||
|
|
||
|
|
||
| imran = Person("Imran", date(2004, 8, 31), "Ubuntu") | ||
| print(imran.is_adult()) | ||
|
|
||
| # 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. | ||
|
|
||
| # Update the is_adult method to act the same as before. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # Write a Person class using @datatype which uses a datetime.date for date of birth, rather than an int for age. | ||
| from dataclasses import dataclass | ||
| from datetime import date | ||
|
|
||
| @dataclass(frozen=True) | ||
| class Person: | ||
| name: str | ||
| dob: date | ||
| preferred_operating_system: str | ||
|
|
||
| # Re-add the is_adult method to it. | ||
| def is_adult(self): | ||
| dob = self.dob | ||
| today = date.today() | ||
| if dob.year > today.year - 18: return False | ||
| if dob.year == today.year - 18: | ||
| if dob.month > today.month: return False | ||
| if dob.month == today.month: | ||
| if dob.day > today.day: return False | ||
| return True | ||
|
|
||
|
|
||
| imran = Person("Imran", date(2004, 8, 31), "Ubuntu") | ||
| print(imran.is_adult()) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| from dataclasses import dataclass | ||
| from typing import List | ||
|
|
||
| @dataclass(frozen=True) | ||
| class Person: | ||
| name: str | ||
| children: List["Person"] | ||
| age: int | ||
|
|
||
| fatma = Person(name="Fatma", children=[], age=82) | ||
| aisha = Person(name="Aisha", children=[], age=83) | ||
|
|
||
| imran = Person(name="Imran", children=[fatma, aisha], age=102) | ||
|
|
||
| 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) | ||
|
|
||
| # Fix the above code so that it works. | ||
| # You must not change the print on line 17 - we do want to print the children’s ages. | ||
| # (Feel free to invent the ages of Imran’s children.) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| 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 == 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}") | ||
|
|
||
| # Try changing the type annotation of Person.preferred_operating_system from str to List[str]. | ||
| # Run mypy on the code. | ||
|
|
||
| # exercise_10.py:29: error: Argument "preferred_operating_system" to "Person" has incompatible type "str"; expected "list[str]" [arg-type] | ||
| # exercise_10.py:30: error: Argument "preferred_operating_system" to "Person" has incompatible type "str"; expected "list[str]" [arg-type] | ||
|
|
||
|
|
||
| # It tells us different places that our code is now wrong, because we’re passing values of the wrong type. | ||
| # We probably also want to rename our field - lists are plural. Rename the field to preferred_operating_systems. | ||
| # Run mypy again. | ||
|
|
||
| # exercise_10.py:23: error: "Person" has no attribute "preferred_operating_system"; maybe "preferred_operating_systems"? [attr-defined] | ||
| # exercise_10.py:29: error: Unexpected keyword argument "preferred_operating_system" for "Person"; did you mean "preferred_operating_systems"? [call-arg] | ||
| # exercise_10.py:30: error: Unexpected keyword argument "preferred_operating_system" for "Person"; did you mean "preferred_operating_systems"? [call-arg] | ||
|
|
||
| # Fix all of the places that mypy tells you need changing. Make sure the program works as you’d expect. | ||
|
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. When I run this I get two empty lists. Is that the expected output? |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| # Write a program which: | ||
|
|
||
| # Already has a list of Laptops that a library has to lend out. | ||
| # Accepts user input to create a new Person - it should use the input function to read a person’s name, age, and preferred operating system. | ||
| # Tells the user how many laptops the library has that have that operating system. | ||
| # If there is an operating system that has more laptops available, tells the user that if they’re willing to accept that operating system they’re more likely to get a laptop. | ||
|
|
||
| # You should convert the age and preferred operating system input from the user into more constrained types as quickly as possible, | ||
| # and should output errors to stderr and terminate the program with a non-zero exit code if the user input bad values. | ||
|
|
||
| # ----- Imports | ||
| from dataclasses import dataclass | ||
| from enum import Enum | ||
| import sys | ||
|
|
||
| # ----- Classes and Enums | ||
| class OperatingSystem(Enum): | ||
| MACOS = "macOS" | ||
| ARCH = "Arch Linux" | ||
| UBUNTU = "Ubuntu" | ||
| WINDOWS = "Windows" | ||
|
|
||
| @dataclass(frozen=True) | ||
| class Person: | ||
| name: str | ||
| age: int | ||
| preferred_operating_system: OperatingSystem | ||
|
|
||
| @dataclass(frozen=True) | ||
| class Laptop: | ||
| id: int | ||
| operating_system: OperatingSystem | ||
|
|
||
| # ----- Functions | ||
| def count_laptops(laptops: list[Laptop], laptop_counts: dict[str, int]): | ||
| for laptop in laptops: | ||
| laptop_counts[laptop.operating_system] += 1 | ||
|
|
||
| def check_laptop_abundance(user: Person, laptop_counts: dict[OperatingSystem, int]): | ||
| abundant_laptops = [] | ||
| for laptop in laptop_counts: | ||
| if laptop_counts[laptop] > laptop_counts[user.preferred_operating_system]: | ||
| abundant_laptops.append(laptop.value) | ||
| if len(abundant_laptops) > 0: | ||
| print("\nIf you are willing to accept another operating system you may get a laptop sooner.") | ||
| print("We have more laptops available with the following OS:") | ||
| for laptop in abundant_laptops: | ||
| print(laptop) | ||
|
|
||
| # ----- Data and Constants | ||
| laptops = [ | ||
| Laptop(id=1, operating_system=OperatingSystem.ARCH), | ||
| Laptop(id=2, operating_system=OperatingSystem.ARCH), | ||
| Laptop(id=3, operating_system=OperatingSystem.UBUNTU), | ||
| Laptop(id=4, operating_system=OperatingSystem.UBUNTU), | ||
| Laptop(id=5, operating_system=OperatingSystem.UBUNTU), | ||
| Laptop(id=6, operating_system=OperatingSystem.UBUNTU), | ||
| Laptop(id=7, operating_system=OperatingSystem.MACOS), | ||
| Laptop(id=8, operating_system=OperatingSystem.MACOS), | ||
| ] | ||
| laptop_counts = { | ||
| OperatingSystem.MACOS: 0, | ||
| OperatingSystem.ARCH: 0, | ||
| OperatingSystem.UBUNTU: 0, | ||
| OperatingSystem.WINDOWS: 0 | ||
| } | ||
|
|
||
| # ----- Script | ||
| user_name = input("Please enter your full name:\n") | ||
| user_age_str = input("Please enter your age:\n") | ||
|
|
||
| try: | ||
| user_age = int(user_age_str) | ||
| except ValueError: | ||
| sys.exit("Error: Age should be a number.") | ||
|
|
||
| user_operating_system_str = input("Please enter your preferred operating system (options: ARCH, UBUNTU, MACOS, WINDOWS):\n") | ||
|
|
||
| if user_operating_system_str not in OperatingSystem.__members__: | ||
| sys.exit("Error: Operating system should be written in all caps from given options.") | ||
|
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. Is there any change you could make to the UX to make this more forgiving, rather than requiring the user to write their input in such a strict format? |
||
| else: user_operating_system = OperatingSystem[user_operating_system_str] | ||
|
|
||
| user = Person(user_name, user_age, user_operating_system) | ||
|
|
||
| count_laptops(laptops, laptop_counts) | ||
| print(f"\nThe number of available laptops with {user_operating_system.value} is: {laptop_counts[user_operating_system]}") | ||
|
|
||
| check_laptop_abundance(user, laptop_counts) | ||
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.
Could you expand on these explanations or think of any other benefits / drawbacks?