primeiro commit com exercicios de Exercism realizados até esta data

This commit is contained in:
2026-10-17 22:10:27 +01:00
commit 58fccbe27a
708 changed files with 35038 additions and 0 deletions
@@ -0,0 +1,24 @@
{
"authors": [
"j08k"
],
"contributors": [
"valentin-p",
"bethanyG",
"mukeshgurpude",
"kotp"
],
"files": {
"solution": [
"dicts.py"
],
"test": [
"dicts_test.py"
],
"exemplar": [
".meta/exemplar.py"
]
},
"icon": "high-scores",
"blurb": "Learn about dicts by managing warehouse inventory."
}
@@ -0,0 +1 @@
{"track":"python","exercise":"inventory-management","id":"cd6171c10c1d4c5c8ff039bc0d4af80f","url":"https://exercism.org/tracks/python/exercises/inventory-management","handle":"sanctushck","is_requester":true,"auto_approve":false}
+130
View File
@@ -0,0 +1,130 @@
# Help
## Running the tests
We use [pytest][pytest: Getting Started Guide] as our website test runner.
You will need to install `pytest` on your development machine if you want to run tests for the Python track locally.
You should also install the following `pytest` plugins:
- [pytest-cache][pytest-cache]
- [pytest-subtests][pytest-subtests]
Extended information can be found in our website [Python testing guide][Python track tests page].
### Running Tests
To run the included tests, navigate to the folder where the exercise is stored using `cd` in your terminal (_replace `{exercise-folder-location}` below with your path_).
Test files usually end in `_test.py`, and are the same tests that run on the website when a solution is uploaded.
Linux/MacOS
```bash
$ cd {path/to/exercise-folder-location}
```
Windows
```powershell
PS C:\Users\foobar> cd {path\to\exercise-folder-location}
```
<br>
Next, run the `pytest` command in your terminal, replacing `{exercise_test.py}` with the name of the test file:
Linux/MacOS
```bash
$ python3 -m pytest -o markers=task {exercise_test.py}
==================== 7 passed in 0.08s ====================
```
Windows
```powershell
PS C:\Users\foobar> py -m pytest -o markers=task {exercise_test.py}
==================== 7 passed in 0.08s ====================
```
### Common options
- `-o` : override default `pytest.ini` (_you can use this to avoid marker warnings_)
- `-v` : enable verbose output.
- `-x` : stop running tests on first failure.
- `--ff` : run failures from previous test before running other test cases.
For additional options, use `python3 -m pytest -h` or `py -m pytest -h`.
### Fixing warnings
If you do not use `pytest -o markers=task` when invoking `pytest`, you might receive a `PytestUnknownMarkWarning` for tests that use our new syntax:
```bash
PytestUnknownMarkWarning: Unknown pytest.mark.task - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html
```
To avoid typing `pytest -o markers=task` for every test you run, you can use a `pytest.ini` configuration file.
We have made one that can be downloaded from the top level of the Python track directory: [pytest.ini][pytest.ini].
You can also create your own `pytest.ini` file with the following content:
```ini
[pytest]
markers =
task: A concept exercise task.
```
Placing the `pytest.ini` file in the _root_ or _working_ directory for your Python track exercises will register the marks and stop the warnings.
More information on pytest marks can be found in the `pytest` documentation on [marking test functions][pytest: marking test functions with attributes] and the `pytest` documentation on [working with custom markers][pytest: working with custom markers].
Information on customizing pytest configurations can be found in the `pytest` documentation on [configuration file formats][pytest: configuration file formats].
### Extending your IDE or Code Editor
Many IDEs and code editors have built-in support for using `pytest` and other code quality tools.
Some community-sourced options can be found on our [Python track tools page][Python track tools page].
[Pytest: Getting Started Guide]: https://docs.pytest.org/en/latest/getting-started.html
[Python track tools page]: https://exercism.org/docs/tracks/python/tools
[Python track tests page]: https://exercism.org/docs/tracks/python/tests
[pytest-cache]:http://pythonhosted.org/pytest-cache/
[pytest-subtests]:https://github.com/pytest-dev/pytest-subtests
[pytest.ini]: https://github.com/exercism/python/blob/main/pytest.ini
[pytest: configuration file formats]: https://docs.pytest.org/en/6.2.x/customize.html#configuration-file-formats
[pytest: marking test functions with attributes]: https://docs.pytest.org/en/6.2.x/mark.html#raising-errors-on-unknown-marks
[pytest: working with custom markers]: https://docs.pytest.org/en/6.2.x/example/markers.html#working-with-custom-markers
## Submitting your solution
You can submit your solution using the `exercism submit dicts.py` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [Python track's documentation](https://exercism.org/docs/tracks/python)
- The [Python track's programming category on the forum](https://forum.exercism.org/c/programming/python)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
Below are some resources for getting help if you run into trouble:
- [The PSF](https://www.python.org) hosts Python downloads, documentation, and community resources.
- [The Exercism Community on Discord](https://exercism.org/r/discord)
- [Python Community on Discord](https://pythondiscord.com/) is a very helpful and active community.
- [/r/learnpython/](https://www.reddit.com/r/learnpython/) is a subreddit designed for Python learners.
- [#python on Libera.chat](https://www.python.org/community/irc/) this is where the core developers for the language hang out and get work done.
- [Python Community Forums](https://discuss.python.org/)
- [Free Code Camp Community Forums](https://forum.freecodecamp.org/)
- [CodeNewbie Community Help Tag](https://community.codenewbie.org/t/help)
- [Pythontutor](http://pythontutor.com/) for stepping through small code snippets visually.
Additionally, [StackOverflow](http://stackoverflow.com/questions/tagged/python) is a good spot to search for your problem/question to see if it has been answered already.
If not - you can always [ask](https://stackoverflow.com/help/how-to-ask) or [answer](https://stackoverflow.com/help/how-to-answer) someone else's question.
+49
View File
@@ -0,0 +1,49 @@
# Hints
## General
- [The Python Dictionary Tutorial][dict-tutorial] can be a great place to start.
- The Python docs on [Mapping Types - dicts][dict docs] is also pretty helpful.
## 1. Create an inventory based on a list
- You need a [for loop][for-loop] to iterate the list of items, then insert each item in the dictionary if missing and increment the item count using the dictionary accessor.
- You can use [`dict.setdefault`][dict setdefault] to make sure the value is set before incrementing the count of the item.
- This function should [return][return-keyword] a dict].
## 2. Add items from a list to an existing dictionary
- You need a [for loop][for-loop] to iterate the list of items, then insert each item if not already in the dictionary and [increment][increment] the item count using the dictionary accessor.
- You can use [`dict.setdefault`][dict setdefault] to make sure the value is set before incrementing the count of the item.
- The function `add_items` can be used by the `create_inventory` function with an empty dictionary in parameter.
- This function should [return][return-keyword] a dict.
## 3. Decrement items from the inventory
- You need [for loop][for-loop] to iterate the list of items, if the number of items is not `0` then [decrement][decrement] the current number of items.
- You can use the check `key in dict` that returns `True` if the key exists to make sure the value is in the dictionary before decrementing the number of items.
- This function should [return][return-keyword] a dict.
## 4. Remove an item entirely from the inventory
- If item is in the dictionary, [remove it][dict-pop].
- If item is not in the dictionary, do nothing.
- This function should [return][return-keyword] a dict.
## 5. Return the inventory content
- You need to use a [for loop][for-loop] on the inventory and if the number of item is greater of `0` then append the `tuple` to a `list`.
- You can use [`dict.items()`][dict items] to iterate on both the item and the value at the same time, `items()` returns a `tuple` that you can use or deconstruct, if needed.
- This function should [return][return-keyword] a [list][list] of [tuples][tuples].
[decrement]: https://www.w3schools.com/python/gloss_python_assignment_operators.asp
[dict docs]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
[dict items]: https://docs.python.org/3/library/stdtypes.html#dict.items
[dict setdefault]: https://www.w3schools.com/python/ref_dictionary_setdefault.asp
[dict-pop]: https://www.w3schools.com/python/ref_dictionary_pop.asp
[dict-tutorial]: https://docs.python.org/3/tutorial/datastructures.html#dictionaries
[for-loop]: https://docs.python.org/3/tutorial/controlflow.html#for-statements
[increment]: https://www.w3schools.com/python/gloss_python_assignment_operators.asp
[list]: https://docs.python.org/3/tutorial/introduction.html#lists
[return-keyword]: https://www.w3schools.com/python/ref_keyword_return.asp
[tuples]: https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences
+247
View File
@@ -0,0 +1,247 @@
# Inventory Management
Welcome to Inventory Management on Exercism's Python Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
## Introduction
A dictionary (`dict`) in Python is a data structure that associates [hashable][term-hashable] _keys_ to _values_ and is known in other programming languages as a resizable [hash table][hashtable-wikipedia], hashmap, or [associative array][associative-array].
Dictionaries are Python's only built-in [mapping type][mapping-types-dict].
`Keys` must be hashable and unique across the dictionary.
Key types can include `numbers`, `str`, or `tuples` (of _immutable_ values).
They cannot contain _mutable_ data structures such as `lists`, `dict`s, or `set`s.
As of Python 3.7, `dict` key order is guaranteed to be the order in which entries are inserted.
`values` can be of any data type or structure.
Values can also nest _arbitrarily_, so they can include lists-of-lists, sub-dictionaries, and other custom or compound data structures.
Given a `key`, dictionaries can retrieve a `value` in (on average) constant time (_independent of the number of entries_).
Compared to searching for a value within a `list` or `array` (_without knowing the `index` position_), a `dict` uses significantly more memory, but has very rapid retrieval.
Dictionaries are especially useful in scenarios where the collection of items is large and must be accessed and updated frequently.
## Dictionary Construction
Dictionaries can be created in many ways.
The two most straightforward are using the `dict()`constructor or declaring a `dict` _literal_.
### The `dict()` Class Constructor
`dict()` (_the constructor for the dictionary class_) can be used with any iterable of `key`, `value` pairs or with a series of `<name>=<value>` _arguments_:
```python
#Passing a list of key,value tuples.
>>> wombat = dict([('name', 'Wombat'),('speed', 23),('land_animal', True)])
{'name': 'Wombat', 'speed': 23, 'land_animal': True}
#Using key=value arguments.
>>> bear = dict(name="Black Bear", speed=40, land_animal=True)
{'name': 'Black Bear', 'speed': 40, 'land_animal': True}
```
### Dictionary Literals
A `dict` can also be directly entered as a _dictionary literal_, using curly brackets (`{}`) enclosing `key : value` pairs:
```python
>>> whale = {"name": "Blue Whale", "speed": 35, "land_animal": False}
{'name': 'Blue Whale', 'speed': 35, 'land_animal': False}
```
## Accessing Values in a Dictionary
You can access an entry in a dictionary using a _key_ in square (`[]`) brackets.
If a `key` does not exist n the `dict`, a `KeyError` is thrown:
```python
>>> bear["speed"]
40
>>> bear["color"]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'color'
```
Accessing an entry via the `.get(<key>, <default value>)` method can avoid the `KeyError`:
```python
>>> bear.get("color", 'not found')
'not found'
```
## Changing or Adding Dictionary Values
You can change an entry `value` by assigning to its _key_:
```python
#Assigning the value "Grizzly Bear" to the name key.
>>> bear["name"] = "Grizzly Bear"
{'name': 'Grizzly Bear', 'speed': 40, 'land_animal': True}
>>> whale["speed"] = 25
{'name': 'Blue Whale', 'speed': 25, 'land_animal': False}
```
New `key`:`value` pairs can be _added_ in the same fashion:
```python
# Adding an new "color" key with a new "tawney" value.
>>> bear["color"] = 'tawney'
{'name': 'Grizzly Bear', 'speed': 40, 'land_animal': True, 'color': 'tawney'}
>>> whale["blowholes"] = 1
{'name': 'Blue Whale', 'speed': 25, 'land_animal': False, 'blowholes': 1}
```
## Removing (Pop-ing) Dictionary Entries
You can use the `.pop(<key>)` method to delete a dictionary entry.
`.pop()` removes the (`key`, `value`) pair and returns the `value` for use.
Like `.get()`, `.pop(<key>)` accepts second argument (_`dict.pop(<key>, <default value>)`_) that will be returned if the `key` is not found.
This prevents a `KeyError` being raised:
```python
#Using .pop() removes both the key and value, returning the value.
>>> bear.pop("name")
'Grizzly Bear'
#The "name" key is now removed from the dictionary.
#Attempting .pop() a second time will throw a KeyError.
>>> bear.pop("name")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'name'
#Using a default argument with .pop() will prevent a KeyError from a missing key.
>>> bear.pop("name", "Unknown")
'Unknown'
```
## Looping through/Iterating over a Dictionary
Looping through a dictionary using `for item in dict` or `while item` will iterate over only the _keys_ by default.
You can access the _values_ within the same loop by using _square brackets_:
```python
>>> for key in bear:
>>> print((key, bear[key])) #this forms a tuple of (key, value) and prints it.
('name', 'Black Bear')
('speed', 40)
('land_animal', True)
```
You can also use the `.items()` method, which returns (`key`, `value`) tuples automatically:
```python
#dict.items() forms (key, value tuples) that can be unpacked and iterated over.
>>> for key, value in whale.items():
>>> print(key, ":", value)
name : Blue Whale
speed : 25
land_animal : False
blowholes : 1
```
Likewise, the `.keys()` method will return `keys` and the `.values()` method will return the `values`.
[associative-array]: https://en.wikipedia.org/wiki/Associative_array#:~:text=In%20computer%20science%2C%20an%20associative,a%20function%20with%20finite%20domain.
[hashtable-wikipedia]: https://en.wikipedia.org/wiki/Hash_table
[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
[term-hashable]: https://docs.python.org/3/glossary.html#term-hashable
## Instructions
In this exercise, you will be managing an inventory system.
The inventory should be organized by the item name and it should keep track of the number of items available.
You will have to handle adding items to an inventory.
Each time an item appears in a given list, the item's quantity should be increased by `1` in the inventory.
You will also have to handle deleting items from an inventory by decreasing quantities by `1` when requested.
Finally, you will need to implement a function that will return all the key-value pairs in a given inventory as a `list` of `tuples`.
## 1. Create an inventory based on a list
Implement the `create_inventory(<input list>)` function that creates an "inventory" from an input list of items.
It should return a `dict` containing each item name paired with their respective quantity.
```python
>>> create_inventory(["coal", "wood", "wood", "diamond", "diamond", "diamond"])
{"coal":1, "wood":2, "diamond":3}
```
## 2. Add items from a list to an existing dictionary
Implement the `add_items(<inventory dict>, <item list>)` function that adds a list of items to the passed-in inventory:
```python
>>> add_items({"coal":1}, ["wood", "iron", "coal", "wood"])
{"coal":2, "wood":2, "iron":1}
```
## 3. Decrement items from the inventory
Implement the `decrement_items(<inventory dict>, <items list>)` function that takes a `list` of items.
Your function should remove `1` from an item count for each time that item appears on the `list`:
```python
>>> decrement_items({"coal":3, "diamond":1, "iron":5}, ["diamond", "coal", "iron", "iron"])
{"coal":2, "diamond":0, "iron":3}
```
Item counts in the inventory should not be allowed to fall below 0.
If the number of times an item appears on the input `list` exceeds the count available, the quantity listed for that item should remain at 0.
Additional requests for removing counts should be ignored once the count falls to zero.
```python
>>> decrement_items({"coal":2, "wood":1, "diamond":2}, ["coal", "coal", "wood", "wood", "diamond"])
{"coal":0, "wood":0, "diamond":1}
```
## 4. Remove an entry entirely from the inventory
Implement the `remove_item(<inventory dict>, <item>)` function that removes an item and its count entirely from an inventory:
```python
>>> remove_item({"coal":2, "wood":1, "diamond":2}, "coal")
{"wood":1, "diamond":2}
```
If the item is not found in the inventory, the function should return the original inventory unchanged.
```python
>>> remove_item({"coal":2, "wood":1, "diamond":2}, "gold")
{"coal":2, "wood":1, "diamond":2}
```
## 5. Return the entire content of the inventory
Implement the `list_inventory(<inventory dict>)` function that takes an inventory and returns a list of `(item, quantity)` tuples.
The list should only include the _available_ items (_with a quantity greater than zero_):
```python
>>> list_inventory({"coal":7, "wood":11, "diamond":2, "iron":7, "silver":0})
[('coal', 7), ('diamond', 2), ('iron', 7), ('wood', 11)]
```
## Source
### Created by
- @j08k
### Contributed to by
- @valentin-p
- @bethanyG
- @mukeshgurpude
- @kotp
+80
View File
@@ -0,0 +1,80 @@
"""Functions to keep track and alter inventory."""
def create_inventory(items):
"""Create a dict that tracks the amount (count) of each element on the `items` list.
:param items: list - list of items to create an inventory from.
:return: dict - the inventory dictionary.
"""
inventory: dict = {}
for item in items:
inventory[item] = items.count(item)
return inventory
def add_items(inventory, items):
"""Add or increment items in inventory using elements from the items `list`.
:param inventory: dict - dictionary of existing inventory.
:param items: list - list of items to update the inventory with.
:return: dict - the inventory updated with the new items.
"""
# inventory is empty, create new one and return inventory
if inventory == {}:
return create_inventory(items)
# increment items that already exists in inventory
for item in inventory:
inventory[item] += items.count(item)
# add new items that doesnt exist in inventory
# the new items can only be added after incrementing items that already
# exists in inventory to avoid adding the new items again
for item in items:
if item not in inventory:
inventory[item] = items.count(item)
return inventory
def decrement_items(inventory, items):
"""Decrement items in inventory using elements from the `items` list.
:param inventory: dict - inventory dictionary.
:param items: list - list of items to decrement from the inventory.
:return: dict - updated inventory with items decremented.
"""
for item in inventory:
if item in items:
inventory[item] -= items.count(item)
if inventory[item] < 0:
inventory[item] = 0
return inventory
def remove_item(inventory, item):
"""Remove item from inventory if it matches `item` string.
:param inventory: dict - inventory dictionary.
:param item: str - item to remove from the inventory.
:return: dict - updated inventory with item removed. Current inventory if item does not match.
"""
if item in inventory:
inventory.pop(item)
return inventory
def list_inventory(inventory):
"""Create a list containing only available (item_name, item_count > 0) pairs in inventory.
:param inventory: dict - an inventory dictionary.
:return: list of tuples - list of key, value pairs from the inventory dictionary.
"""
inventory_list: list[tuple] = []
# conventional
# for item in inventory:
# if inventory[item] > 0:
# inventory_list.append((item, inventory[item]))
# using list comprehension
inventory_list = [
(item, inventory[item]) for item in inventory if inventory[item] > 0
]
return inventory_list
+120
View File
@@ -0,0 +1,120 @@
import unittest
import pytest
from dicts import (create_inventory,
add_items,
decrement_items,
remove_item,
list_inventory)
class InventoryTest(unittest.TestCase):
@pytest.mark.task(taskno=1)
def test_create_inventory(self):
actual_result = create_inventory(["wood", "iron", "iron", "diamond", "diamond"])
expected = {"wood": 1, "iron": 2, "diamond": 2}
error_message = ('Called create_inventory(["wood", "iron", "iron", "diamond", "diamond"]). '
f'The function returned {actual_result}, but the tests expected {expected}.')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=2)
def test_add_one_item(self):
actual_result = add_items({"wood": 4, "iron": 2}, ["iron", "iron"])
expected = {"wood": 4, "iron": 4}
error_message = ('Called add_items({"wood": 4, "iron": 2}, ["iron", "iron"]). '
f'The function returned {actual_result}, but the tests expected {expected}.')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=2)
def test_add_multiple_items(self):
actual_result = add_items({"wood": 2, "gold": 1, "diamond": 3}, ["wood", "gold", "gold"])
expected = {"wood": 3, "gold": 3, "diamond": 3}
error_message = ('Called add_items({"wood": 2, "gold": 1, "diamond": 3}, ["wood", "gold", "gold"]). '
f'The function returned {actual_result}, but the tests expected {expected}.')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=2)
def test_add_new_item(self):
actual_result = add_items({"iron": 1, "diamond": 2}, ["iron", "wood", "wood"])
expected = {"iron": 2, "diamond": 2, "wood": 2}
error_message = ('Called add_items({"iron": 1, "diamond": 2}, ["iron", "wood", "wood"]). '
f'The function returned {actual_result}, but the tests expected {expected}.')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=2)
def test_add_from_empty_dict(self):
actual_result = add_items({}, ["iron", "iron", "diamond"])
expected = {"iron": 2, "diamond": 1}
error_message = ('Called add_items({}, ["iron", "iron", "diamond"]). '
f'The function returned {actual_result}, but the tests expected {expected}.')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=3)
def test_decrement_items(self):
actual_result = decrement_items({"iron": 3, "diamond": 4, "gold": 2},
["iron", "iron", "diamond", "gold", "gold"])
expected = {"iron": 1, "diamond": 3, "gold": 0}
error_message = ('Called decrement_items({"iron": 3, "diamond": 4, "gold": 2},'
'["iron", "iron", "diamond", "gold", "gold"]). The function '
f'returned {actual_result}, but the tests expected {expected}.')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=3)
def test_not_below_zero(self):
actual_result = decrement_items({"wood": 2, "iron": 3, "diamond": 1},
["wood", "wood", "wood", "iron", "diamond", "diamond"])
expected = {"wood": 0, "iron": 2, "diamond": 0}
error_message = ('Called decrement_items({"wood": 2, "iron": 3, "diamond": 1}, '
'["wood", "wood", "wood", "iron", "diamond", "diamond"]). The '
f'function returned {actual_result}, but the tests expected {expected}.')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=3)
def test_decrement_items_not_in_inventory(self):
actual_result = decrement_items({"iron": 3, "gold": 2},
["iron", "wood", "iron", "diamond"])
expected = {"iron": 1, "gold": 2}
error_message = ('Called decrement_items({"iron": 3, "gold": 2}, '
'["iron", "wood", "iron", "diamond"]). The function '
f'returned {actual_result}, but the tests '
f'expected {expected}.')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=4)
def test_remove_item(self):
actual_result = remove_item({"iron": 1, "diamond": 2, "gold": 1}, "diamond")
expected = {"iron": 1, "gold": 1}
error_message = ('Called remove_item({"iron": 1, "diamond": 2, "gold": 1}, "diamond"). '
f'The function returned {actual_result}, but the tests expected {expected}.')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=4)
def test_remove_item_not_in_inventory(self):
actual_result = remove_item({"iron": 1, "diamond": 2, "gold": 1}, "wood")
expected = {"iron": 1, "gold": 1, "diamond": 2}
error_message = ('Called remove_item({"iron": 1, "diamond": 2, "gold": 1}, "wood"). '
f'The function returned {actual_result}, '
f'but the tests expected {expected}.')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=5)
def test_list_inventory(self):
actual_result = list_inventory({"coal": 15, "diamond": 3, "wood": 67, "silver": 0})
expected = [("coal", 15), ("diamond", 3), ("wood", 67)]
error_message = ('Called list_inventory({"coal": 15, "diamond": 3, "wood": 67, "silver": 0}). '
f'The function returned {actual_result}, '
f'but the tests expected {expected}.')
self.assertEqual(actual_result, expected, msg=error_message)