Skip to content
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

Fix exercises on objects #114

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 22 additions & 25 deletions object_oriented_programming.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1068,7 +1068,6 @@
"cell_type": "markdown",
"id": "a2cc7735-1d30-47b3-a172-96201e26f98d",
"metadata": {
"jp-MarkdownHeadingCollapsed": true,
"tags": []
},
"source": [
Expand All @@ -1080,13 +1079,19 @@
"id": "94f8c9c0-0f4e-4452-88eb-62ff87d218e9",
"metadata": {},
"source": [
"Define a class `Scoop` that represents a single scoop of ice cream. Each scoop should have a **single** attribute, `flavor`, a string that you can initialize when you create the instance of `Scoop`.\n",
"Define a class `Scoop` that represents a single scoop of ice cream.\n",
"Each scoop should have a **single** attribute, `flavor`, a string that you can initialize when you create the instance of `Scoop`.\n",
"\n",
"Define also a `__str__` method to return a string reprensentation of a scoop.\n",
"The output should be `Ice cream scoop with flavor '<flavor>'`, where `<flavor>` is the actual scoop's flavor.\n",
"**Pay attention to the single quotes!**\n",
"\n",
"Define also a `__str__` method to return a string reprensentation of a scoop. The output should be `Ice cream scoop with flavor '<flavor>'`, where `<flavor` is the actual scoop's flavor. **Pay attention to the single quotes!**\n",
"Modify the `solution_ice_cream_scop` function to return a list of tuples each containing a Scoop instance and its flavor.\n",
"The list of flavors will be provided to you as an argument to the function.\n",
"\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 to create instances of the <code>Scoop</code> class with the following example flavors: chocolate, vanilla, persimmon. This function should return a <strong>list of tuples</strong> of the kind <code>(Scoop, __str__)</code>, where <code>__str__</code> stands for the <strong>string representations</strong> of the ice cream <code>Scoop</code> instance.\n",
"</div>"
]
},
Expand All @@ -1100,23 +1105,21 @@
"outputs": [],
"source": [
"%%ipytest\n",
"\n",
"class Scoop:\n",
" \"\"\"A class representing a single scoop of ice cream\"\"\"\n",
" # Write your class implementation here\n",
" \n",
"\n",
"\n",
"flavors = # TODO: create a tuple containing the flavors\n",
"\n",
"def solution_ice_cream_scoop(flavors: tuple[str]) -> list[str]:\n",
"def solution_ice_cream_scoop(flavors: tuple[str]) -> list[tuple]:\n",
" # Write your solution here\n",
" pass"
" return"
]
},
{
"cell_type": "markdown",
"id": "824ca183-251f-47bc-9e47-a780872c5ecf",
"metadata": {
"jp-MarkdownHeadingCollapsed": true,
"tags": []
},
"source": [
Expand All @@ -1128,25 +1131,19 @@
"id": "4244ff19-7bbf-43cc-9d1e-eba13f7f5e4e",
"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 `Bowl` that can hold many ice cream scoops, as many as you like. You should use the `Scoop` class you created in the previous exercise.\n",
"\n",
"The `Bowl` class should have a method called `add_scoops()` that accept **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",
" In the <code>__init__</code> method of the <code>Bowl</code> class, you should define an attribute that acts as a container to hold all 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 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 to create a bowl of scoops with flavors chocolate, vanilla, and stracciatella, for example. The function should output a <strong>tuple</strong> containing the <code>Bowl</code> instance and its content (a string). The bowl content should be formatted as follows: <code>Ice cream bowl with chocolate, vanilla, stracciatella scoops</code>\n",
"</div>"
]
},
{
Expand All @@ -1159,16 +1156,15 @@
"outputs": [],
"source": [
"%%ipytest\n",
"\n",
"class Bowl:\n",
" \"\"\"A class representing a bowl of ice cream scoops\"\"\"\n",
" # Write your class implementation here\n",
"\n",
"\n",
"flavors = # TODO: create a tuple containing the flavors\n",
" \n",
"def solution_ice_cream_bowl(flavors: tuple[str]) -> str:\n",
"def solution_ice_cream_bowl(flavors: tuple[str]) -> tuple[object, str]:\n",
" # Write your solution here\n",
" pass"
" return"
]
},
{
Expand Down Expand Up @@ -1295,6 +1291,7 @@
"cell_type": "markdown",
"id": "09ef6028-b6a6-4f79-a418-bce3e3e3f60f",
"metadata": {
"jp-MarkdownHeadingCollapsed": true,
"tags": []
},
"source": [
Expand Down Expand Up @@ -1545,7 +1542,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.0"
"version": "3.11.3"
}
},
"nbformat": 4,
Expand Down
24 changes: 19 additions & 5 deletions tutorial/tests/test_object_oriented_programming.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
from numpy import average


FLAVORS = [
("chocolate",),
("chocolate", "vanilla", "persimmon"),
("chocolate", "vanilla", "stracciatella"),
("chocolate", "vanilla", "stracciatella", "strawberry"),
("chocolate", "vanilla", "stracciatella", "strawberry", "pistachio"),
]

#
# Exercise 1: Ice cream scoop
#
Expand All @@ -21,9 +29,15 @@ def __str__(self):
return f"Ice cream scoop with flavor '{self.flavor}'"


def test_ice_cream_scoop(function_to_test) -> None:
flavors = ("chocolate", "vanilla", "persimmon")
assert function_to_test(flavors) == [str(Scoop(flavor)) for flavor in flavors]
def reference_ice_cream_scoop(flavors: tuple[str]) -> list[Scoop, str]:
return [(Scoop(flavor), str(Scoop(flavor))) for flavor in flavors]


@pytest.mark.parametrize("flavors", FLAVORS)
def test_ice_cream_scoop(flavors, function_to_test) -> None:
test_solution = [string for _, string in function_to_test(flavors)]
reference_solution = [string for _, string in reference_ice_cream_scoop(flavors)]
assert test_solution == reference_solution
Comment on lines +38 to +40
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been thinking about this test all afternoon (mostly). And it can be faked easily because it doesn't really enforce you to define/use the class. One can build a tuple with (None, "<Properly formatted string>") and the test will pass.

I think it's trickier that it seems to perform some checks on the actual class. One way is to parse the AST as Simone did with some FP tests, but I didn't want to implement it for the time being.

Needs some more thoughts... 💭

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could check whether there is a class with the name Scoop in the locals() of the function. Something like:

import inspect
import ast

def check_if_class_in_scope(class_name: str, scope: callable) -> bool:
    source = ast.parse(inspect.getsource(scope))
    for node in ast.walk(source):
        match node:
            case ast.ClassDef() as cd:
                return cd.name == class_name


def f1():
    class A:
        a = 1


is_a = check_if_class_in_scope("A", f1)  # True
is_b = check_if_class_in_scope("B", f1)  # True
print(f"A is {is_a}, B is {is_b}")

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good suggestion 👍🏻 We must change the solution and add the class inside the solution function. Or apply the same idea to the entire code of the cell.



#
Expand Down Expand Up @@ -62,7 +76,7 @@ def read_data(name: str, data_dir: str = "data") -> pathlib.Path:
"""Read input data"""
current_module = sys.modules[__name__]
return (
pathlib.Path(current_module.__file__).parent / f"{data_dir}/{name}"
pathlib.Path(current_module.__file__).parent / f"{data_dir}/{name}"
).resolve()


Expand Down Expand Up @@ -172,7 +186,7 @@ def __init__(self, universe_start: str) -> None:
def evolve(self) -> "Universe":
"""Evolve the universe"""
for n, moon_i in enumerate(self.moons[:-1]):
for moon_j in self.moons[n + 1:]:
for moon_j in self.moons[n + 1 :]:
moon_i.update_velocities(moon_j)

for moon in self.moons:
Expand Down