Skip to content

Commit 85dd229

Browse files
author
Shehab Abdel-Salam
committed
Fix OOP exercises
1 parent 153c504 commit 85dd229

File tree

10 files changed

+183
-61
lines changed

10 files changed

+183
-61
lines changed

chapters/chapter10_oop/exercises/exercise_68.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
# v2 = Vector(4, 5, 6)
1111
# v3 = v1 + v2
1212
# v4 = v1 - v2
13+
from __future__ import annotations
1314

1415

1516
class Vector3D:
Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,32 @@
1-
# Exercise 69 - Calculator Class
2-
# Create a class called Calculator. The class should have two methods: add and subtract.
3-
# The two methods should be class methods and should take two arguments each.
1+
# Exercise 69 - TaxCalculator Class
2+
# Define a `TaxCalculator` class that calculates the tax for a given income.
3+
# The class should have a method `calculate_tax` that takes an income as a parameter and returns the tax amount.
4+
# The tax should be calculated based on the following rules:
5+
# - 0% tax for income less than or equal to 10,000
6+
# - 10% tax for income greater than 10,000 and less than or equal to 50,000
7+
# - 20% tax for income greater than 50,000 and less than or equal to 100,000
8+
# - 30% tax for income greater than 100,000
49

10+
# Example:
11+
# tax_calculator = TaxCalculator()
12+
# tax_calculator.calculate_tax(5000) => 0
13+
# tax_calculator.calculate_tax(15000) => 500
14+
# tax_calculator.calculate_tax(75000) => 10000
515

6-
class Calculator: ...
16+
17+
class TaxCalculator:
18+
def __init__(self):
19+
self.income = 0
20+
21+
def calculate_tax(self, income: int) -> float:
22+
self.income = income
23+
if self.income <= 10000:
24+
return 0
25+
elif self.income <= 50000:
26+
return self.income * 0.1
27+
elif self.income <= 100000:
28+
return self.income * 0.2
29+
elif self.income > 100000:
30+
return self.income * 0.3
31+
else:
32+
return 0

chapters/chapter10_oop/exercises/exercise_70.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
# circle = Circle(5)
1111
# assert circle.area() == 78.54
1212

13+
from abc import ABC, abstractmethod
14+
15+
PI = 3.14159
16+
1317

1418
class Shape:
1519
# Your code should go here.

chapters/chapter10_oop/exercises/exercise_71.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Exercise 71 - Library Management System [Part 1/4]
1+
# Exercise 71 - Library Management System [Part 1/3]
22
# In the next three exercises, you will implement a simple library management system.
33
# Let's start first by defining our core classes (Book and Author).
44

chapters/chapter10_oop/exercises/exercise_72.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
# Exercise 72 - Library Management System [Part 2/4]
2-
# In the previous exercise, you defined the core classes for a library management system.
1+
# Exercise 72 - Library Management System [Part 2/3]
32
# In this exercise, you will implement the `BookShelf` class that will hold a collection of books.
43

54
# The `BookShelf` class should have the following methods:
@@ -9,16 +8,20 @@
98
# 3. `get_books() -> List[Book]`: Return a list of all the books on the shelf.
109
# 4. `get_books_by_genre(genre: Genre) -> List[Book]`: Return a list of books by genre.
1110

12-
# Note: Define any additional methods or attributes that you think are necessary for the `Shelf` class.
11+
# Note: Feel free to define any additional methods or attributes that
12+
# you think are necessary for the `BookShelf` class.
1313

14-
# Example of the expected behavior:
14+
# Example:
1515
# author = Author("John", "Doe")
1616
# book1 = Book("The Book", author, 2021, Genre.Fiction)
1717
# shelf = BookShelf([book1])
1818
# book2 = Book("Another Book", author, 2022, Genre.Fiction)
1919
# shelf.add_book(book2)
2020
# assert shelf.get_books() == [book1, book2]
2121
# assert shelf.get_books_by_genre(Genre.Fiction) == [book1, book2]
22+
# shelf.remove_book(book1)
23+
24+
from .exercise_71 import Book, Genre
2225

2326

2427
class BookShelf:
Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,35 @@
1-
# Exercise 73 - Library Management System [Part 3/4]
2-
# In the previous two exercises, you defined the core classes for a library management system.
1+
# Exercise 73 - Library Management System [Part 3/3]
32
# In this exercise, you will implement the `Library` class that will hold a collection of shelves.
43

54
# The `Library` class should have the following methods:
65
# 1. `add_shelf(shelf: BookShelf)`: Add a shelf to the library.
76
# 2. `remove_shelf(shelf: BookShelf)`: Remove a shelf from the library.
8-
# 3. `get_books() -> List[Book]`: Return a list of all the books in the library.
7+
# 3. `get_books() -> List[Book]`: Return a list of all the books from all the shelves in the library.
98
# 4. `is_book_in_library(book: Book) -> bool`: Return `True` if the book is in the library, otherwise `False`.
109

1110

12-
# Note: Define any additional methods or attributes that you think are necessary for the `Library` class.
11+
# Note: Feel free to define any additional methods or attributes that
12+
# you think are necessary for the `BookShelf` class.
13+
14+
15+
# Example:
16+
# author = Author("John", "Doe")
17+
# book1 = Book("The Book", author, 2021, Genre.Fiction)
18+
# shelf1 = BookShelf([book1])
19+
# book2 = Book("Another Book", author, 2022, Genre.Fiction)
20+
# shelf2 = BookShelf([book2])
21+
# library = Library()
22+
# library.add_shelf(shelf1)
23+
# library.add_shelf(shelf2)
24+
# assert library.get_books() == [book1, book2]
25+
# assert library.is_book_in_library(book1) is True
26+
# assert library.is_book_in_library(Book("Non Existing Book", author, 2021, Genre.Fiction)) is False
27+
# library.remove_shelf(shelf1)
28+
# assert library.get_books() == [book2]
29+
from .exercise_71 import Book, Genre
30+
from .exercise_72 import BookShelf
31+
32+
1333
class Library:
1434
# Add your code here.
1535
...

chapters/chapter10_oop/exercises/exercise_74.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,30 @@
1-
# Exercise 74 - Banking System [Part 1/3]
1+
# Exercise 74 - Banking System [Part 1/2]
22
# In the next three exercises, you will implement a simple banking system.
33
# Let's start first by defining our core classes (Card, Account, and Customer).
44

5-
# Create two dataclasses called `Card`, `Account`, and a class called `Customer`.
6-
# The `Card` class should have the following attributes:
7-
# 1. card_number
8-
# 2. card_holder
9-
# 3. expiration_date
10-
# 4. pin
5+
# Create three dataclasses called `Card`, `Account`, and `Customer`.
116

7+
# The `Card` class should have the following attributes:
8+
# 1. card_number (a string)
9+
# 2. card_holder (a string)
10+
# 3. expiration_date (a string)
11+
# 4. pin (a string)
1212

1313
# The `Account` class should have the following attributes:
14-
# 1. account_number
15-
# 2. account_holder
16-
# 3. balance
14+
# 1. account_number (a string)
15+
# 2. account_holder (an instance of the `Customer` class)
16+
# 3. balance (a float)
17+
# 4. cards (a list of `Card` instances)
18+
19+
# The `Customer` class should have the following attributes:
20+
# 1. first_name (a string)
21+
# 2. last_name (a string)
22+
# 3. accounts (a list of `Account` instances)
1723
# 4. cards (a list of `Card` instances)
24+
# 5. total_balance (a float)
1825

19-
from dataclasses import dataclass
26+
from __future__ import annotations
27+
from dataclasses import dataclass, field
2028

2129

2230
@dataclass

chapters/chapter10_oop/exercises/exercise_75.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
1-
# Exercise 75 - Banking System [Part 2/3]
2-
# So far we've defined the `Card` and `Account` classes. Now, let's define the `Customer` class.
1+
# Exercise 75 - Banking System [Part 2/2]
2+
# So far we've defined the `Card` and `Account` classes. Now, let's define the `BankCustomer` class.
33

44

5-
# The `Customer` class should have the following attributes:
6-
# 1. first_name
7-
# 2. last_name
8-
# 3. accounts (a list of `Account` instances)
9-
# 4. cards (a list of `Card` instances)
10-
# 5. total_balance (a float)
11-
12-
# The `Customer` class should also have the following methods:
5+
# The `BankCustomer` class should also have the following methods:
136
# 1. add_account(self, account: Account) -> None: adds an account to the accounts list.
147
# 2. remove_account(self, account_number: int) -> None: removes an account from the accounts list.
158
# 3. add_card(self, card: Card) -> None: adds a card to the cards list.
@@ -20,7 +13,7 @@
2013
# 6. withdraw(self, account_number: int, amount: float, card_number: int, pin: int) -> None: withdraws money from an account.
2114
# 7. deposit(self, account_number: int, amount: float) -> None: deposits money into an account.
2215

23-
from .exercise_74 import Account, Card
16+
from .exercise_74 import Account, Card, Customer
2417

2518

2619
class Customer:
Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,31 @@
1-
# Exercise 77 - Protocol
1+
# Exercise 77 - Content Creators
2+
# We have two types of Content Creators: Bloggers and Vloggers.
3+
# Both of them should have a method called `create_content` that calls the appropriate method to create content.
4+
# We want to define a protocol called `ContentCreator` that has the `create_content` method.
5+
6+
from typing import Protocol
7+
8+
9+
class ContentCreator(Protocol):
10+
def create_content(self) -> str: ...
11+
12+
13+
class Blogger:
14+
15+
def add_post(self, title: str):
16+
return f"Creating a new post: {title}"
17+
18+
# TODO: Write create_content() method.
19+
20+
21+
class Vlogger:
22+
23+
def add_video(self, title: str) -> None:
24+
return f"Creating a new video: {title} with path: /videos/{title}.mp4"
25+
26+
# TODO: Write create_content() method.
27+
28+
29+
def create_content(creator: ContentCreator, title: str) -> str:
30+
# Your code here
31+
...

chapters/chapter10_oop/tests/test_ch10.py

Lines changed: 62 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1+
import pytest
12
from ..exercises.exercise_68 import Vector3D
2-
from ..exercises.exercise_69 import Calculator
3+
from ..exercises.exercise_69 import TaxCalculator
34
from ..exercises.exercise_70 import Circle, Shape, Square
45
from ..exercises.exercise_71 import Author, Book, Genre
56
from ..exercises.exercise_72 import BookShelf
67
from ..exercises.exercise_73 import Library
7-
from ..exercises.exercise_74 import Account, Card
8-
from ..exercises.exercise_75 import Customer
8+
from ..exercises.exercise_74 import Account, Card, Customer
9+
from ..exercises.exercise_75 import BankCustomer
910
from ..exercises.exercise_76 import Box
11+
from ..exercises.exercise_77 import create_content, Blogger, Vlogger
1012

11-
# from ..exercises.exercise_77 import
1213
# from ..exercises.exercise_78 import
1314

1415

@@ -28,10 +29,14 @@ def test_e68():
2829

2930

3031
def test_e69():
31-
assert Calculator.add(0, 0) == 0
32-
assert Calculator.add(1, 2) == 3
33-
assert Calculator.subtract(0, 0) == 0
34-
assert Calculator.subtract(1, 2) == -1
32+
tax_calculator = TaxCalculator()
33+
assert tax_calculator.calculate_tax(0) == 0
34+
assert tax_calculator.calculate_tax(10_000) == 0
35+
assert tax_calculator.calculate_tax(10_001) == 1000.1
36+
assert tax_calculator.calculate_tax(50_000) == 5000
37+
assert tax_calculator.calculate_tax(50_001) == 10_000.2
38+
assert tax_calculator.calculate_tax(100_000) == 20_000
39+
assert tax_calculator.calculate_tax(150_000) == 45_000
3540

3641

3742
def test_e70():
@@ -59,20 +64,23 @@ def test_e72():
5964
book = Book("The Book", author, 2024, Genre.Fiction)
6065

6166
shelf = BookShelf()
62-
shelf.add_book(book)
6367
assert shelf.get_books() == []
64-
assert shelf.get_books_by_genre(Genre.Fiction) == []
68+
shelf.add_book(book)
6569
assert shelf.get_books() == [book]
70+
assert shelf.get_books_by_genre(Genre.History) == []
71+
assert shelf.get_books_by_genre(Genre.NonFiction) == []
6672
assert shelf.get_books_by_genre(Genre.Fiction) == [book]
6773

6874
shelf.remove_book(book)
6975
assert shelf.get_books() == []
7076
assert shelf.get_books_by_genre(Genre.Fiction) == []
71-
shelf.add_book(Book("Biography Book", author, 2024, Genre.Biography))
72-
shelf.add_book(Book("History Book", author, 2024, Genre.History))
77+
book2 = Book("Biography Book", author, 2024, Genre.Biography)
78+
book3 = Book("History Book", author, 2024, Genre.History)
79+
shelf.add_book(book2)
80+
shelf.add_book(book3)
7381
assert shelf.get_books_by_genre(Genre.Fiction) == []
74-
assert shelf.get_books_by_genre(Genre.Biography) == [book]
75-
assert shelf.get_books_by_genre(Genre.History) == [book]
82+
assert shelf.get_books_by_genre(Genre.Biography) == [book2]
83+
assert shelf.get_books_by_genre(Genre.History) == [book3]
7684

7785

7886
def test_e73():
@@ -93,7 +101,8 @@ def test_e73():
93101
assert library.get_books() == []
94102
library.add_shelf(shelfA)
95103
library.add_shelf(shelfB)
96-
assert library.get_books() == shelf_A_books + shelf_B_books
104+
assert set(library.get_books()) == set(shelf_A_books + shelf_B_books)
105+
97106
assert library.is_book_in_library(shelf_A_books[0]) is True
98107
assert (
99108
library.is_book_in_library(
@@ -102,25 +111,48 @@ def test_e73():
102111
is False
103112
)
104113
library.remove_shelf(shelfA)
105-
assert library.get_books() == shelf_B_books
114+
assert set(library.get_books()) == set(shelf_B_books)
106115

107116

108117
def test_e74():
109-
# TODO
110-
card = Card("1234", "User A", "2024-12-31", "1234")
118+
customer = Customer("John", "Doe")
119+
card = Card(
120+
card_number="1234",
121+
customer=customer,
122+
expiry_date="2024-12-31",
123+
pin="1234",
124+
)
111125
assert isinstance(card, Card)
112-
account = Account("1234", "User A", 1000, [card])
126+
account = Account("1234", customer, 1000, [card])
113127
assert isinstance(account, Account)
128+
assert account.account_number == "1234"
114129

115130

116-
# TODO
117131
def test_e75():
118-
customer = Customer("John", "Doe")
119-
assert isinstance(customer, Customer)
120-
assert customer.add_account(Account("1234", "User A", 1000, [])) is None
121-
assert customer.add_card(Card("1234", "User A", "2024-12-31", "1234")) is None
132+
customer = BankCustomer("John", "Doe")
133+
card1 = Card("1234", customer, "2024-12-31", "1234")
134+
card2 = Card("5678", customer, "2024-12-31", "5678")
135+
account1 = Account("acc-1234", customer, 1000, [card1])
136+
account2 = Account("acc-5678", customer, 2000, [card2])
137+
138+
assert customer.get_total_balance() == 0.0
139+
customer.add_account(account1)
122140
assert customer.get_total_balance() == 1000
123-
assert customer.withdraw(1234, 500, 1234, 1234) is None
141+
customer.add_account(account2)
142+
assert customer.get_total_balance() == 3000
143+
assert customer.get_card("1234") == card1
144+
assert customer.get_card("5678") == card2
145+
assert customer.get_account("acc-1234") == account1
146+
assert customer.get_account("acc-5678") == account2
147+
148+
assert customer.deposit("acc-1234", 500) is None
149+
assert customer.get_account("acc-1234").balance == 1500
150+
assert customer.get_total_balance() == 3500
151+
152+
with pytest.raises(ValueError):
153+
customer.withdraw("acc-1234", 1_000_000, "1234", "1234")
154+
with pytest.raises(ValueError):
155+
customer.withdraw("acc-1234", 1_000, "1234", "WRONG_PIN")
124156

125157

126158
def test_e76():
@@ -137,3 +169,8 @@ def test_e76():
137169
box3.add("Hello")
138170
assert isinstance(box3.get(), str)
139171
assert box3.get() == "Hello"
172+
173+
174+
def test_e77():
175+
assert create_content(Blogger()) == "Blogger is creating content"
176+
assert create_content(Vlogger()) == "Vlogger is creating content"

0 commit comments

Comments
 (0)