feat(curriculum): add musical instrument workshop (#61750)

Co-authored-by: Dario <105294544+Dario-DC@users.noreply.github.com>
Co-authored-by: Ilenia M <nethleen@gmail.com>
Co-authored-by: Ilenia <26656284+ilenia-magoni@users.noreply.github.com>
This commit is contained in:
Zaira
2025-09-25 15:45:46 +02:00
committed by GitHub
co-authored by Dario Ilenia M Ilenia
parent 6263791d2d
commit 8a404bface
17 changed files with 774 additions and 3 deletions
+5 -3
View File
@@ -4496,9 +4496,11 @@
"title": "Classes and Objects",
"intro": ["Learn about Classes and Objects in these lectures."]
},
"workshop-placeholder-classes-and-objects": {
"title": "Placeholder - waiting for title",
"intro": [""]
"workshop-musical-instrument-inventory": {
"title": "Build a Musical Instrument Inventory",
"intro": [
"In this workshop, you will learn about classes, objects, and methods in Python by building a simple musical instrument inventory."
]
},
"lab-budget-app": {
"title": "Build a Budget App",
@@ -0,0 +1,9 @@
---
title: Introduction to the Build a Musical Instrument Inventory
block: workshop-musical-instrument-inventory
superBlock: full-stack-developer
---
## Introduction to the Build a Musical Instrument Inventory
In this workshop, you will learn about classes, objects, and methods in Python by building a simple musical instrument inventory.
@@ -0,0 +1,41 @@
---
id: 6894c1b715b8d18a18ef3203
title: Step 1
challengeType: 20
dashedName: step-1
---
# --description--
In this workshop, you'll get some practice with Python's classes and objects by building a musical instrument inventory.
To define a class in Python, use the `class` keyword followed by the class name and a colon.
```python
class ClassName:
# class contents go here
```
Remember that in Python, class names typically use PascalCase, which means each word starts with an uppercase letter with no spaces between words.
Start by creating a class called `MusicalInstrument`. Add `pass` as the body of the class for now.
# --hints--
You should create a class called `MusicalInstrument`.
```js
({
test: () => assert(runPython(`_Node(_code).has_class("MusicalInstrument")`))
});
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
--fcc-editable-region--
```
@@ -0,0 +1,50 @@
---
id: 6895dffc64e97df0e62e9ef9
title: Step 2
challengeType: 20
dashedName: step-2
---
# --description--
Methods are functions defined inside a class that can perform actions using the class's data. Here is how you create methods:
```python
def method_name(parameter1, parameter2,...):
# Method contents will go here
```
In Python, the `__init__` method is a special method known as the initializer method, which is called when you create a new instance of the class.
Inside your `MusicalInstrument` class, add an `__init__` method with three parameters: `self`, `name`, and `instrument_type`.
The `self` parameter is a standard way to refer to the instance of the class and is required as the first parameter for all instance methods in a class.
# --hints--
You should define an `__init__` method inside your `MusicalInstrument` class.
```js
({
test: () => assert(runPython(`_Node(_code).find_class("MusicalInstrument").has_function("__init__")`))
});
```
Your `__init__` method should have three parameters: `self`, `name`, and `instrument_type`.
```js
({
test: () => assert(runPython(`_Node(_code).find_class("MusicalInstrument").find_function("__init__").has_args("self, name, instrument_type")`))
});
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
class MusicalInstrument:
pass
--fcc-editable-region--
```
@@ -0,0 +1,43 @@
---
id: 6895e0452b3370f26f365a46
title: Step 3
challengeType: 20
dashedName: step-3
---
# --description--
In the `__init__` method, you need to assign the parameters to instance attributes. Instance attributes are variables that belong to a specific instance of a class.
Inside your `__init__` method, assign the `name` parameter to `self.name` and the `instrument_type` parameter to `self.instrument_type`.
# --hints--
You should assign the `name` parameter to `self.name` inside the `__init__` method.
```js
({
test: () => assert(runPython(`_Node(_code).find_class("MusicalInstrument").find_function("__init__").find_body()[0].is_equivalent("self.name = name")`))
});
```
You should assign the `instrument_type` parameter to `self.instrument_type` inside the `__init__` method.
```js
({
test: () => assert(runPython(`_Node(_code).find_class("MusicalInstrument").find_function("__init__").find_body()[1].is_equivalent("self.instrument_type = instrument_type")`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
class MusicalInstrument:
def __init__(self, name, instrument_type):
pass
--fcc-editable-region--
```
@@ -0,0 +1,57 @@
---
id: 6895e0bdb0348bf46952ab67
title: Step 4
challengeType: 20
dashedName: step-4
---
# --description--
Now it's time to create instances of your `MusicalInstrument` class.
To create an instance of a class, you call the class name like a function, passing the required arguments for the `__init__` method (except for `self`, which is automatically handled by Python).
```python
my_instance = ClassName(arg1, arg2)
```
When you create this instance, Python calls your `__init__` method and stores the values as instance attributes that belong only to this specific instance.
Outside the class definition, create an instance named `instrument_1` for an `Oboe` which is a `woodwind` instrument.
# --hints--
You should create an instance of the `MusicalInstrument` class named `instrument_1`.
```js
({
test: () => runPython(`
assert isinstance(instrument_1, MusicalInstrument)
`)
})
```
You should pass `Oboe` and `woodwind` as arguments when creating `instrument_1`.
```js
({
test: () => runPython(`
assert instrument_1.name == "Oboe"
assert instrument_1.instrument_type == "woodwind"
`)
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
class MusicalInstrument:
def __init__(self, name, instrument_type):
self.name = name
self.instrument_type = instrument_type
--fcc-editable-region--
```
@@ -0,0 +1,51 @@
---
id: 6895e197180c9af6988e1e19
title: Step 5
challengeType: 20
dashedName: step-5
---
# --description--
Create a second instance named `instrument_2` for a `Trumpet` which is a `brass` instrument.
Notice how you can create multiple instances from the same class template. Each instance stores its own attribute values independently from other instances.
# --hints--
You should create an instance named `instrument_2` of the `MusicalInstrument` class.
```js
({
test: () => runPython(`
assert isinstance(instrument_2, MusicalInstrument)
`)
})
```
You should pass `Trumpet` and `brass` as arguments when creating `instrument_2`.
```js
({
test: () => runPython(`
assert instrument_2.name == "Trumpet"
assert instrument_2.instrument_type == "brass"
`)
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
class MusicalInstrument:
def __init__(self, name, instrument_type):
self.name = name
self.instrument_type = instrument_type
instrument_1 = MusicalInstrument('Oboe', 'woodwind')
--fcc-editable-region--
```
@@ -0,0 +1,69 @@
---
id: 6895e235935106f88281b648
title: Step 6
challengeType: 20
dashedName: step-6
---
# --description--
You can access the attributes of your instances using dot notation:
```py
instance_name.attribute_name
```
This allows you to retrieve the values that were stored during initialization.
Try printing the `name` and `instrument_type` attributes of both your instances. You'll be able to see the values you assigned when you created them.
# --hints--
You should print the `name` attribute of `instrument_1`.
```js
({
test: () => assert(runPython(`_Node(_code).has_call("print(instrument_1.name)")`))
})
```
You should print the `instrument_type` attribute of `instrument_1`.
```js
({
test: () => assert(runPython(`_Node(_code).has_call("print(instrument_1.instrument_type)")`))
});
```
You should print the `name` attribute of `instrument_2`.
```js
({
test: () => assert(runPython(`_Node(_code).has_call("print(instrument_2.name)")`))
});
```
You should print the `instrument_type` attribute of `instrument_2`.
```js
({
test: () => assert(runPython(`_Node(_code).has_call("print(instrument_2.instrument_type)")`))
});
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
class MusicalInstrument:
def __init__(self, name, instrument_type):
self.name = name
self.instrument_type = instrument_type
instrument_1 = MusicalInstrument('Oboe', 'woodwind')
instrument_2 = MusicalInstrument('Trumpet', 'brass')
--fcc-editable-region--
```
@@ -0,0 +1,67 @@
---
id: 6895e2becb7890fad818ad4d
title: Step 7
challengeType: 20
dashedName: step-7
---
# --description--
Now that you understand how to access object attributes, remove the existing `print` calls.
# --hints--
You should remove the `print(instrument_1.name)` statement.
```js
({
test: () => assert.isFalse(runPython(`_Node(_code).has_call("print(instrument_1.name)")`))
})
```
You should remove the `print(instrument_1.instrument_type)` statement.
```js
({
test: () => assert.isFalse(runPython(`_Node(_code).has_call("print(instrument_1.instrument_type)")`))
});
```
You should remove the `print(instrument_2.name)` statement.
```js
({
test: () => assert.isFalse(runPython(`_Node(_code).has_call("print(instrument_2.name)")`))
});
```
You should remove the `print(instrument_2.instrument_type)` statement.
```js
({
test: () => assert.isFalse(runPython(`_Node(_code).has_call("print(instrument_2.instrument_type)")`))
});
```
# --seed--
## --seed-contents--
```py
class MusicalInstrument:
def __init__(self, name, instrument_type):
self.name = name
self.instrument_type = instrument_type
instrument_1 = MusicalInstrument('Oboe', 'woodwind')
instrument_2 = MusicalInstrument('Trumpet', 'brass')
--fcc-editable-region--
print(instrument_1.name)
print(instrument_1.instrument_type)
print(instrument_2.name)
print(instrument_2.instrument_type)
--fcc-editable-region--
```
@@ -0,0 +1,39 @@
---
id: 6895e356fd877efd6e3657ee
title: Step 8
challengeType: 20
dashedName: step-8
---
# --description--
Inside the `MusicalInstrument` class, define another method named `play` with only the `self` parameter. The `self` parameter allows methods to access the specific instance's attributes.
# --hints--
You should define a method named `play` with a `self` parameter.
```js
({
test: () => assert(runPython(`_Node(_code).find_class("MusicalInstrument").find_function("play").has_args("self")`))
});
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
class MusicalInstrument:
def __init__(self, name, instrument_type):
self.name = name
self.instrument_type = instrument_type
--fcc-editable-region--
instrument_1 = MusicalInstrument('Oboe', 'woodwind')
instrument_2 = MusicalInstrument('Trumpet', 'brass')
```
@@ -0,0 +1,42 @@
---
id: 6895e3f91c069e0068a42582
title: Step 9
challengeType: 20
dashedName: step-9
---
# --description--
Inside your `play` method, use `print()` to display a message about playing the instrument.
Use an f-string to include the instrument's name in the message: `The [instrument name] is fun to play!`. The `[instrument name]` should be replaced with the actual name of the instrument.
# --hints--
Your `play` method should use an f-string to display the message `The [instrument name] is fun to play!`. Use `self.name` to access the instrument name.
```js
({
test: () => assert(runPython(`_Node(_code).find_class("MusicalInstrument").find_function("play").has_call('print(f"The {self.name} is fun to play!")')`))
});
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
class MusicalInstrument:
def __init__(self, name, instrument_type):
self.name = name
self.instrument_type = instrument_type
def play(self):
pass
--fcc-editable-region--
instrument_1 = MusicalInstrument('Oboe', 'woodwind')
instrument_2 = MusicalInstrument('Trumpet', 'brass')
```
@@ -0,0 +1,53 @@
---
id: 6895e4940e39950288e406c2
title: Step 10
challengeType: 20
dashedName: step-10
---
# --description--
Next, add another method to your class named `get_fact` that returns a fact about the instrument.
This method should have only the `self` parameter and return an f-string that says `The [instrument name] is part of the [instrument type] family of instruments.`.
Unlike the `play` method which prints a message directly, this method should return a string that can be used elsewhere in your code.
# --hints--
You should define a method named `get_fact` with a `self` parameter.
```js
({
test: () => assert(runPython(`_Node(_code).find_class("MusicalInstrument").find_function("get_fact").has_args("self")`))
});
```
Your `get_fact` method should return an f-string containing the instrument's name and type.
```js
({
test: () => assert(runPython(`_Node(_code).find_class("MusicalInstrument").find_function("get_fact").find_return().is_equivalent("return f'The {self.name} is part of the {self.instrument_type} family of instruments.'")`))
});
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
class MusicalInstrument:
def __init__(self, name, instrument_type):
self.name = name
self.instrument_type = instrument_type
def play(self):
print(f'The {self.name} is fun to play!')
--fcc-editable-region--
instrument_1 = MusicalInstrument('Oboe', 'woodwind')
instrument_2 = MusicalInstrument('Trumpet', 'brass')
```
@@ -0,0 +1,52 @@
---
id: 6895e4e023e07603bcef46f9
title: Step 11
challengeType: 20
dashedName: step-11
---
# --description--
Now let's use the methods you've created.
Call the `play()` method on your `instrument_1` instance. This will print the message about playing the Oboe.
To call a method on an instance, use the dot notation:
```python
instance_name.method_name()
```
# --hints--
You should call the `play()` method on the `instrument_1` instance.
```js
({
test: () => assert(runPython(`_Node(_code).has_stmt("instrument_1.play()")`))
});
```
# --seed--
## --seed-contents--
```py
class MusicalInstrument:
def __init__(self, name, instrument_type):
self.name = name
self.instrument_type = instrument_type
def play(self):
print(f'The {self.name} is fun to play!')
def get_fact(self):
return f'The {self.name} is part of the {self.instrument_type} family of instruments.'
--fcc-editable-region--
instrument_1 = MusicalInstrument('Oboe', 'woodwind')
instrument_2 = MusicalInstrument('Trumpet', 'brass')
--fcc-editable-region--
```
@@ -0,0 +1,48 @@
---
id: 6895e5337b1914050b0af7bd
title: Step 12
challengeType: 20
dashedName: step-12
---
# --description--
Now, call the `get_fact()` method on the `instrument_1` instance and print the result.
Since `get_fact()` returns a string instead of printing it directly, you need to use `print()` to display the returned string.
# --hints--
You should call `get_fact()` on `instrument_1` and print the result.
```js
({
test: () => assert(runPython(`_Node(_code).has_call("print(instrument_1.get_fact())")`))
});
```
# --seed--
## --seed-contents--
```py
class MusicalInstrument:
def __init__(self, name, instrument_type):
self.name = name
self.instrument_type = instrument_type
def play(self):
print(f'The {self.name} is fun to play!')
def get_fact(self):
return f'The {self.name} is part of the {self.instrument_type} family of instruments.'
--fcc-editable-region--
instrument_1 = MusicalInstrument('Oboe', 'woodwind')
instrument_2 = MusicalInstrument('Trumpet', 'brass')
instrument_1.play()
--fcc-editable-region--
```
@@ -0,0 +1,83 @@
---
id: 6895e572df8596062234f571
title: Step 13
challengeType: 20
dashedName: step-13
---
# --description--
Finally, do the same for `instrument_2`. Call the `play()` method on `instrument_2`, and then print the result of calling `get_fact()` on `instrument_2`.
This will complete your musical instrument workshop!
# --hints--
You should call the `play()` method on the `instrument_2` instance.
```js
({
test: () => assert(runPython(`_Node(_code).has_stmt("instrument_2.play()")`))
});
```
You should call `get_fact()` on `instrument_2` and print the result.
```js
({
test: () => assert(runPython(`_Node(_code).has_call("print(instrument_2.get_fact())")`))
});
```
# --seed--
## --seed-contents--
```py
class MusicalInstrument:
def __init__(self, name, instrument_type):
self.name = name
self.instrument_type = instrument_type
def play(self):
print(f'The {self.name} is fun to play!')
def get_fact(self):
return f'The {self.name} is part of the {self.instrument_type} family of instruments.'
--fcc-editable-region--
instrument_1 = MusicalInstrument('Oboe', 'woodwind')
instrument_2 = MusicalInstrument('Trumpet', 'brass')
instrument_1.play()
print(instrument_1.get_fact())
--fcc-editable-region--
```
# --solutions--
```py
class MusicalInstrument:
def __init__(self, name, instrument_type):
self.name = name
self.instrument_type = instrument_type
def play(self):
print(f'The {self.name} is fun to play!')
def get_fact(self):
return f'The {self.name} is part of the {self.instrument_type} family of instruments.'
instrument_1 = MusicalInstrument('Oboe', 'woodwind')
instrument_2 = MusicalInstrument('Trumpet', 'brass')
instrument_1.play() # The Oboe is fun to play!
print(instrument_1.get_fact()) # The Oboe is part of the woodwind family of instruments.
instrument_2.play() # The Trumpet is fun to play!
print(instrument_2.get_fact()) # The Trumpet is part of the brass family of instruments.
```
@@ -0,0 +1,64 @@
{
"name": "Build a Musical Instrument Inventory",
"isUpcomingChange": true,
"dashedName": "workshop-musical-instrument-inventory",
"helpCategory": "Python",
"challengeOrder": [
{
"id": "6894c1b715b8d18a18ef3203",
"title": "Step 1"
},
{
"id": "6895dffc64e97df0e62e9ef9",
"title": "Step 2"
},
{
"id": "6895e0452b3370f26f365a46",
"title": "Step 3"
},
{
"id": "6895e0bdb0348bf46952ab67",
"title": "Step 4"
},
{
"id": "6895e197180c9af6988e1e19",
"title": "Step 5"
},
{
"id": "6895e235935106f88281b648",
"title": "Step 6"
},
{
"id": "6895e2becb7890fad818ad4d",
"title": "Step 7"
},
{
"id": "6895e356fd877efd6e3657ee",
"title": "Step 8"
},
{
"id": "6895e3f91c069e0068a42582",
"title": "Step 9"
},
{
"id": "6895e4940e39950288e406c2",
"title": "Step 10"
},
{
"id": "6895e4e023e07603bcef46f9",
"title": "Step 11"
},
{
"id": "6895e5337b1914050b0af7bd",
"title": "Step 12"
},
{
"id": "6895e572df8596062234f571",
"title": "Step 13"
}
],
"usesMultifileEditor": true,
"hasEditableBoundaries": true,
"blockLayout": "challenge-grid",
"blockType": "workshop"
}
@@ -703,6 +703,7 @@
"comingSoon": true,
"blocks": [
"lecture-classes-and-objects",
"workshop-musical-instrument-inventory",
"lab-budget-app",
"review-classes-and-objects",
"quiz-classes-and-objects"