Skip to content

Commit

Permalink
Expand OOP introductory notebook (#213)
Browse files Browse the repository at this point in the history
Co-authored-by: despadam <[email protected]>
Co-authored-by: Edoardo Baldi <[email protected]>
  • Loading branch information
3 people authored Mar 27, 2024
1 parent 823c146 commit ffbc6c8
Show file tree
Hide file tree
Showing 3 changed files with 363 additions and 150 deletions.
199 changes: 142 additions & 57 deletions object_oriented_programming.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,20 @@
"metadata": {},
"source": [
"# Table of Contents\n",
"\n",
"- [References](#References)\n",
"- [A `class` as a blueprint of objects](#A-class-as-a-blueprint-of-objects)\n",
"- [Properties and methods](#Properties-and-methods)\n",
"- [Python's *special methods*](#Python's-*special-methods*)\n",
" - [`__str__` and `__repr__`](#__str__-and-__repr__)\n",
" - [Exercise 1: Ice cream scoop 🌶️](#Exercise-1:-Ice-cream-scoop-🌶️)\n",
" - [Comparison methods](#Comparison-methods)\n",
" - [Exercise 2: Ice cream bowl 🌶️🌶️](#Exercise-2:-Ice-cream-bowl-🌶️🌶️)\n",
"- [The `@property` keyword](#The-@property-keyword)\n",
"- [Quick glossary](#Quick-glossary)\n",
"- [Exercise 3: Intcode computer 🌶️🌶️🌶️](#Exercise-3:-Intcode-computer-🌶️🌶️🌶️)"
" - [Object-oriented Programming](#Object-oriented-Programming)\n",
" - [References](#References)\n",
" - [A `class` as a blueprint of objects](#A-class-as-a-blueprint-of-objects)\n",
" - [Properties and methods](#Properties-and-methods)\n",
" - [Python's *special methods*](#Python's-*special-methods*)\n",
" - [`__str__` and `__repr__`](#__str__-and-__repr__)\n",
" - [Exercise 1: Ice cream scoop 🌶️](#Exercise-1:-Ice-cream-scoop-🌶️)\n",
" - [Exercise 2: Ice cream bowl 🌶️🌶️](#Exercise-2:-Ice-cream-bowl-🌶️🌶️)\n",
" - [Comparison methods](#Comparison-methods)\n",
" - [Exercise 3: Ice cream shop 🌶️🌶️🌶️](#Exercise-3:-Ice-cream-shop-🌶️🌶️🌶️)\n",
" - [The `@property` keyword](#The-@property-keyword)\n",
" - [Quick glossary](#Quick-glossary)\n",
" - [Quiz](#Quiz)\n",
" - [Exercise 4: Intcode computer 🌶️🌶️🌶️🌶️](#Exercise-4:-Intcode-computer-🌶️🌶️🌶️🌶️)"
]
},
{
Expand Down Expand Up @@ -270,7 +272,7 @@
"id": "0f394fcf-6dca-4867-b098-51e97adcd268",
"metadata": {},
"source": [
"Special methods are methods that Python define automatically. If you define a custom class, then you are responsible of definining the **expected behavior** of these methods. Otherwise, Python will fallback to the default, built-in definition, or it will raise an error if it doesn't know what to do.\n",
"Special methods are methods that Python defines automatically. If you define a custom class, then you are responsible of defining the **expected behavior** of these methods. Otherwise, Python will fallback to the default, built-in definition, or it will raise an error if it doesn't know what to do.\n",
"\n",
"These are also called **dunder methods**, that is, \"double-underscore methods\", because their names look like `__method__`. There are special **attributes** as well."
]
Expand Down Expand Up @@ -478,7 +480,7 @@
"id": "0d793886-49fd-4300-87e5-664fc7a3eb3c",
"metadata": {},
"source": [
"As you can see we still get that default. That's because here Python is **not** converting `r1` to a string, but instead looking for a string *representation* of the object. It is looking for the [`__repr__` method](https://docs.python.org/3/reference/datamodel.html#object.__repr__), which is defined as\n",
"As you can see we still get the default. That's because here Python is **not** converting `r1` to a string, but instead looking for a string *representation* of the object. It is looking for the [`__repr__` method](https://docs.python.org/3/reference/datamodel.html#object.__repr__), which is defined as\n",
"\n",
"> the “official” string representation of an object\n",
"\n",
Expand Down Expand Up @@ -543,7 +545,7 @@
"\n",
"<div class=\"alert alert-block alert-warning\">\n",
" <h4><b>Question</b></h4>\n",
" Complete the solution function such that it creates <strong>three</strong> instances of the <code>Scoop</code> class with the following flavors: chocolate, vanilla, persimmon. This function should return a list that collects the <strong>string representations</strong> of the ice cream scoops.\n",
" Complete the solution function such that it creates an instance of the <code>Scoop</code> class for every flavor contained in the argument <code>flavors</code>. This function should return a list that collects the <strong>string representations</strong> of the ice cream scoops.\n",
"</div>"
]
},
Expand All @@ -557,14 +559,70 @@
"outputs": [],
"source": [
"%%ipytest\n",
"class Scoop:\n",
" \"\"\"A class representing a single scoop of ice cream\"\"\"\n",
" # Write your class implementation here\n",
"\n",
"def solution_ice_cream_scoop(flavors: tuple[str]) -> list[str]:\n",
"\n",
"flavors = # TODO: create a tuple containing the flavors\n",
" class Scoop:\n",
" \"\"\"A class representing a single scoop of ice cream\"\"\"\n",
" # Write your class implementation here\n",
"\n",
" # Write your solution here\n",
" pass"
]
},
{
"cell_type": "markdown",
"id": "4abfe0ba-c893-4ffd-b40b-5931821bcc62",
"metadata": {
"tags": []
},
"source": [
"### Exercise 2: Ice cream bowl 🌶️🌶️"
]
},
{
"cell_type": "markdown",
"id": "7f366029-6fa1-48f9-95ee-578b02addecd",
"metadata": {},
"source": [
"Create a class `Bowl` that can hold many ice cream scoops, as many as you like. You *should use* the custom class you created in the previous exercise.\n",
"\n",
"The `Bowl` class should have a method called `add_scoops()` that accepts **variable number** of scoops.\n",
"\n",
"<div class=\"alert alert-block alert-info\">\n",
" <h4><b>Hint</b></h4>\n",
" In the <code>__init__</code> method of the <code>Bowl</code> class, you should define an attribute that acts as a container to hold the scoops you might want to add.\n",
"</div>\n",
"\n",
"<div class=\"alert alert-block alert-warning\">\n",
" <h4><b>Question</b></h4> \n",
" Complete the solution function such that it creates a bowl of scoops with every flavor contained in the argument <code>flavors</code>. The output of this function should be a <strong>string</strong> that reports the content of the bowl you just created.\n",
"</div>\n",
"\n",
"For example:\n",
"\n",
"```\n",
"Ice cream bowl with chocolate, vanilla, stracciatella scoops\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0a634345-9799-4d1d-967b-9634c5672e5f",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"%%ipytest\n",
" \n",
"def solution_ice_cream_bowl(flavors: tuple[str]) -> str:\n",
"\n",
" class Bowl:\n",
" \"\"\"A class representing a bowl of ice cream scoops\"\"\"\n",
" # Write your class implementation here\n",
"\n",
"def solution_ice_cream_scoop(flavors: tuple[str]) -> list[str]:\n",
" # Write your solution here\n",
" pass"
]
Expand Down Expand Up @@ -766,7 +824,7 @@
"id": "72254316-200d-4a6d-bf93-d669f041c08b",
"metadata": {},
"source": [
"That's because our `Rectangle` class automatically return `False` if we try to compare for equality an instance of `Rectangle` and any other Python object"
"That's because our `Rectangle` class automatically returns `False` if we try to compare for equality an instance of `Rectangle` and any other Python object."
]
},
{
Expand Down Expand Up @@ -951,58 +1009,50 @@
},
{
"cell_type": "markdown",
"id": "4abfe0ba-c893-4ffd-b40b-5931821bcc62",
"metadata": {
"tags": []
},
"id": "af6a4710",
"metadata": {},
"source": [
"### Exercise 2: Ice cream bowl 🌶️🌶️"
"### Exercise 3: Ice cream shop 🌶️🌶️🌶️"
]
},
{
"cell_type": "markdown",
"id": "7f366029-6fa1-48f9-95ee-578b02addecd",
"id": "883794ff",
"metadata": {},
"source": [
"Create a class `Bowl` that can hold many ice cream scoops, as many as you like. You *should use* the custom class you created in the previous exercise.\n",
"Create a class `Shop` that sells many ice cream flavours. \n",
"\n",
"The `Bowl` class should have a method called `add_scoops()` that accept **variable number** of scoops.\n",
"The `Shop` class should implement the comparison methods `__eq__`, `__lt__`, `__le__`.\n",
"\n",
"<div class=\"alert alert-block alert-info\">\n",
" <h4><b>Hint</b></h4>\n",
" In the <code>__init__</code> method of the <code>Bowl</code> class, you should define an attribute that acts as a container to hold the scoops you might want to add\n",
" <h4><b>Hints</b></h4>\n",
" <ul>\n",
" <li>In the <code>__init__</code> method of the <code>Shop</code> class, you should define an attribute that acts as a container to hold the available flavours.</li>\n",
" <li>You can use <code>__eq__</code> and <code>__lt__</code> to define <code>__le__</code>.</li>\n",
" </ul>\n",
"</div>\n",
"\n",
"<div class=\"alert alert-block alert-warning\">\n",
" <h4><b>Question</b></h4> \n",
" Complete the solution function that creates a bowl of three scoops with flavors chocolate, vanilla, and stracciatella. The output of this function should be a <strong>string</strong> that reports the content of the bowl just created.\n",
"</div>\n",
"\n",
"For example:\n",
"\n",
"```\n",
"Ice cream bowl with chocolate, vanilla, stracciatella scoops\n",
"```"
" Complete the solution function so that it creates two ice cream shops. <code>Shop 1</code> should sell the flavors provided by the parameter <code>flavor_1</code> and <code>Shop 2</code> should sell the flavors provided by the parameter <code>flavor_2</code>. The output of this function should be a <strong>boolean value</strong>: Does <code>Shop 1</code> sell <strong>fewer or the same</strong> number of flavors as <code>Shop 2</code>?\n",
"</div>"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0a634345-9799-4d1d-967b-9634c5672e5f",
"metadata": {
"tags": []
},
"id": "2f203841",
"metadata": {},
"outputs": [],
"source": [
"%%ipytest\n",
"class Bowl:\n",
" \"\"\"A class representing a bowl of ice cream scoops\"\"\"\n",
" # Write your class implementation here\n",
"\n",
"def solution_ice_cream_shop(flavors_1: list[str], flavors_2: list[str]) -> bool:\n",
"\n",
" class Shop:\n",
" \"\"\"A class representing an ice cream shop\"\"\"\n",
" # Write your class implementation here\n",
"\n",
"flavors = # TODO: create a tuple containing the flavors\n",
" \n",
"def solution_ice_cream_bowl(flavors: tuple[str]) -> str:\n",
" # Write your solution here\n",
" pass"
]
Expand Down Expand Up @@ -1165,14 +1215,42 @@
"---"
]
},
{
"cell_type": "markdown",
"id": "027ae22c",
"metadata": {},
"source": [
"## Quiz\n",
"\n",
"Run the following cell to test your knowledge with a small quiz."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "da20c27f",
"metadata": {},
"outputs": [],
"source": [
"from tutorial import object_oriented_programming as oop\n",
"\n",
"oop.OopQuiz()"
]
},
{
"cell_type": "markdown",
"id": "419b5378-ff17-4ed3-8203-97c132918cb7",
"metadata": {
"tags": []
},
"source": [
"## Exercise 3: Intcode computer 🌶️🌶️🌶️"
"## Exercise 4: Intcode computer 🌶️🌶️🌶️🌶️\n",
"\n",
"<div class=\"alert alert-block alert-danger\">\n",
" <h4><b>Note</b></h4>\n",
" This is a recap exercise of intermediate difficulty.\n",
" If you <strong>never</strong> used classes or objects before, please try to solve and understand the previous exercises first.\n",
"</div>"
]
},
{
Expand Down Expand Up @@ -1273,16 +1351,23 @@
},
"outputs": [],
"source": [
"%%ipytest\n",
"class Computer:\n",
" \"\"\"An Intcode computer class\"\"\"\n",
" # Write your class implementation here\n",
"\n",
" \n",
"%%ipytest \n",
"def solution_intcode_computer(intcode: str) -> int:\n",
" class Computer:\n",
" \"\"\"An Intcode computer class\"\"\"\n",
" # Write your class implementation here\n",
"\n",
" # Write your solution function here\n",
" pass"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d107aab6-4225-4407-8613-19d74407c41a",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
Expand All @@ -1301,7 +1386,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.14"
"version": "3.10.13"
}
},
"nbformat": 4,
Expand Down
42 changes: 42 additions & 0 deletions tutorial/object_oriented_programming.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from .common import Question, Quiz


class OopQuiz(Quiz):
def __init__(self, title=""):
q1 = Question(
question="Based on what you learned about Python's special methods, which of the following statements is <strong>true</strong>?",
options={
"__repr__ is also used for __str__, but not vice versa.": "Correct! This statement is true.",
"__str__ is also used for __repr__, but not vice versa.": "The opposite is true.",
"__repr__ and __str__ are completely independent.": "__repr__ is also used for __str__, but not vice versa.",
},
correct_answer="__repr__ is also used for __str__, but not vice versa.",
hint="",
shuffle=True,
)

q2 = Question(
question="Based on what you learned about Python's comparison methods, which of the following statements is <strong>false</strong>?",
options={
"If we implement __gt__, Python will also use it for __lt__": "This statement is true.",
"If we implement __lt__, Python will also use it for __le__": "Correct! This statement is false.",
"If we implement __eq__, Python will also use it for __ne__": "This statement is true.",
},
correct_answer="If we implement __lt__, Python will also use it for __le__",
hint="",
shuffle=True,
)

q3 = Question(
question="Based on what you learned about the @property keyword, which of the following statements is <strong>false</strong>?",
options={
"@property creates attributes that act like methods but can be accessed and assigned as regular attributes.": "This statement is true.",
"@property helps implement attributes that require additional logic or validation when getting or setting their values.": "This statement is true.",
"@property makes code more readable but restricts dynamic attibute behaviour.": "Correct! This statement is false.",
},
correct_answer="@property makes code more readable but restricts dynamic attibute behaviour.",
hint="",
shuffle=True,
)

super().__init__(questions=[q1, q2, q3])
Loading

0 comments on commit ffbc6c8

Please sign in to comment.