- Lab
-
Libraries: If you want this lab, consider one of these libraries.
- Core Tech
Dictionaries in Python
Build a small Python command-line application that organizes trail information with dictionaries. You will create lookup tables, mutate dictionary data safely, translate short planning notes, and combine the finished helpers into a final CLI briefing.
Lab Info
Table of Contents
-
Challenge
Step 1: Explore the project and learning path
Welcome to Dictionaries in Python. You will build the Trail Guide Helper, a compact Python project that uses dictionaries as the main data structure for lookups, updates, and simple text translation. The codebase is intentionally small so you can focus on how mappings behave rather than on framework setup or user interface details.
Why dictionaries matter
Dictionaries are one of the most practical data structures in everyday Python code because they let you attach a meaningful key to a value. That key can represent a status code, a menu item, a configuration flag, a short label, or any other identifier that should map directly to related data.
In production systems, dictionaries often sit behind convenience functions that hide implementation details and keep the rest of the application readable. That is the pattern you will follow here: each module owns a specific type of dictionary data, and the CLI layer composes those helpers into a final report.
Project structure
The starter project is already organized into focused modules:
app/difficulty.pywill hold the trail difficulty lookup functions.app/fees.pywill contain campsite fee creation, mutation, and safe access helpers.app/packing.pywill translate short packing-note codes into full phrases and build a summary dictionary.app/cli.pyis the orchestration layer that will call those helpers and return a formatted report.- The
testsdirectory contains one focused pytest file per hands-on step so you can validate your progress in small increments.
Key terminology
- Dictionary: A Python mapping type that stores values by key instead of by numeric position.
- Key-value pair: One entry in a dictionary, such as
"tent": 25. - Mutation: Changing an existing object in memory, such as adding or removing a dictionary entry.
- Fallback value: A default value returned when requested data is missing.
What you'll accomplish
- Build dictionary literals and dictionaries created from
{}. - Update and delete entries with assignment and
del. - Avoid lookup errors with
getandNonechecks. - Translate short text tokens and assemble a finished CLI briefing. info> This lab experience was developed by the Pluralsight team using Forge, an internally developed AI tool utilizing Gemini technology. All sections were verified by human experts for accuracy prior to publication. For issue reporting, please contact us.
-
Challenge
Step 2: Create and query core lookup dictionaries
This step establishes the project’s reference data. Before the CLI can report anything useful, it needs dependable dictionaries that store trail descriptions and campsite fees in one place. You will start by defining small mappings and then reading one of those mappings by key.
A dictionary is best thought of as a labeled lookup table. Instead of relying on list positions staying perfectly aligned, each value is attached to a descriptive key, which makes code easier to read and much harder to break when data changes. Python dictionary literals use curly braces, and each item is written as
key: value. Once a dictionary exists, you can access a value directly with square brackets if you expect the key to exist, or you can usegetlater when you want safer behavior. In larger applications, these patterns show up in configuration readers, status maps, localization tables, caches, and command dispatchers.In this project,
app/difficulty.pyowns the trail code reference table, whileapp/fees.pyowns campsite prices. The CLI will eventually import both modules, but for now you are building the underlying data helpers that make the final report possible.Key terminology
- Dictionary literal: An inline dictionary definition written with curly braces.
- Direct lookup: Accessing a dictionary value with square bracket syntax.
- Mutable mapping: A mapping object that can be changed after creation.
What you'll accomplish
- Return a dictionary literal of trail difficulty descriptions.
- Read a trail description by key with bracket notation.
- Start from an empty dictionary and add campsite fee entries one by one.
--- ### Task 2.1 Context The first feature in the Trail Guide Helper is a reusable dictionary of trail difficulty codes. Other parts of the application should not hard-code route descriptions in multiple places, because repeated strings are easy to mistype and hard to update consistently.
By centralizing the data in one function, you create a single source of truth that later lookups and tests can share. This is the same pattern used in many production codebases for labels, statuses, and small configuration tables.
Concepts
A Python dictionary is a mapping from unique keys to values. Unlike a list, which is organized by position, a dictionary is organized by identity: you ask for the value attached to a specific key.
Dictionary literals are declared with curly braces and comma-separated
key: valuepairs. Returning the dictionary from a function is important because it lets other code reuse the mapping instead of relying on printed output or duplicated constants. You now have the project’s first mapping in place. The next task uses that same idea from the opposite direction: instead of defining data, you will read from a dictionary with direct key access and observe the default missing-key behavior.--- ### Task 2.2 Context Once a dictionary exists, the next practical skill is retrieving a value by its key. The trail difficulty helper should behave like a thin wrapper around normal dictionary access so other code can ask for a route description without caring how the mapping was created.
Keeping the function small makes the data flow obvious and makes testing straightforward. This style is common in utility modules that sit between raw data and the rest of an application.
Concepts
Square bracket lookup is the standard direct-access mechanism for dictionaries in Python. When the key exists, Python returns the stored value immediately. When the key does not exist, Python raises
KeyError, which is useful when missing data should be treated as an actual mistake rather than silently ignored.This behavior is different from
get, which you will use later when missing data should be handled more gently. With direct lookup working, you are ready to practice building a dictionary dynamically from an empty starting point. That pattern is especially useful when values are computed, loaded, or added one at a time during program execution.--- ### Task 2.3 Context The project also needs a small pricing table for campsite types. In many real programs, a dictionary does not begin life as a fully formed literal; it starts empty and is filled in as data becomes available. Creating the fee mapping this way helps reinforce that dictionaries are mutable objects, not just static constants. It also prepares you for later tasks where you will update and remove entries from the same structure.
Concepts
An empty dictionary is created with
{}. After that, you can add an entry by writing a key inside square brackets on the left side of an assignment and a value on the right side. This syntax mirrors lookup syntax, which is why dictionary mutation feels consistent once you learn it. Because dictionaries store values by key, the order of insertion is less important to this lab than the correctness of each key-value relationship. You completed the project’s foundational dictionaries and confirmed both creation styles: literal definition and incremental population.In the next step, you will mutate those dictionaries in place by updating fees, deleting entries, and reading values safely when a key may or may not exist.
-
Challenge
Step 3: Update, delete, and safely retrieve dictionary data
This step focuses on mutation and safe reads. Real applications rarely use dictionaries as static data forever; they change over time as business rules, user selections, or incoming data shift. You will add a reusable update helper, remove a key when data should no longer appear, and then read values without raising an exception when a key is absent.
Dictionary mutation is powerful because the same object can evolve as the program runs. Assigning to an existing key replaces its value, while assigning to a new key inserts a fresh entry. Deletion removes the key-value pair entirely. These operations are fast and expressive, which is why dictionaries are a common fit for caches, indexes, and configuration registries. But with that flexibility comes the need to handle missing data carefully. Sometimes a missing key should be an error, and sometimes it should be represented as “no value.” Python’s
getmethod supports the second case by returningNoneby default instead of raisingKeyError.Within this project, all of that behavior lives in
app/fees.py. The module acts like a tiny service layer for campsite pricing, while the CLI remains focused on presentation.Key terminology
- Update in place: Modifying the existing dictionary object rather than creating a replacement.
delstatement: The Python keyword used to remove a key from a dictionary.None: Python’s built-in value representing the absence of a real value.
What you'll accomplish
- Add or overwrite a campsite fee with one assignment path.
- Remove a fee entry safely with
del. - Read a fee with
getso missing data returnsNoneinstead of crashing.
--- ### Task 3.1 Context Campground pricing can change, so the project needs a helper that can both insert new fees and replace existing ones. A dedicated update function keeps the rest of the codebase from scattering assignment logic everywhere. That becomes important as projects grow, because a small helper provides one consistent place to change behavior later. In this lab, the CLI will reuse this helper to change the tent fee before generating the final report.
Concepts
Dictionary assignment is dual-purpose: if the key already exists, the value is replaced; if the key does not exist, a new key-value pair is created. This works because dictionaries are mutable mappings. Returning the same dictionary after mutation is a practical testing pattern because callers can immediately inspect the changed state. It also makes the function easier to reuse in simple data-processing pipelines. You have now changed dictionary contents through assignment.
The next task covers the opposite operation: removing an entry entirely when it should no longer appear in later lookups or reports.
--- ### Task 3.2 Context Sometimes data should be removed, not merely overwritten. The campsite fee table needs a helper that can delete an entry so later code behaves as if that site type is no longer listed. Encapsulating the removal logic in one function keeps callers simple and reduces repeated membership checks. This mirrors cleanup logic found in cache invalidation, feature registries, and inventory-style applications.
Concepts
The
delstatement removes an item from a dictionary by key. If you calldelon a key that does not exist, Python raisesKeyError, so it is common to guard the operation with anincheck when a missing key is acceptable. This pattern balances correctness with resilience. After deletion, the dictionary simply no longer has that key, which means future direct lookups will fail and safe lookups will returnNone. The fee table can now grow and shrink.To finish the step, you will add a read helper for the situations where a missing campsite type should produce an absent value instead of an exception.
--- ### Task 3.3 Context Not every lookup failure should stop a program. The Trail Guide Helper will sometimes ask for campsite types that may not be listed, especially after a deletion or when a new type has not been configured yet. A safe retrieval helper gives the caller a predictable result and makes later conditional logic much easier to write. This is particularly useful in user-facing programs where “not found” should become a friendly message rather than a traceback.
Concepts
The dictionary
getmethod returns the stored value for a key if it exists. If the key is missing, it returnsNoneby default instead of raisingKeyError. BecauseNonerepresents the absence of a meaningful value, it is often used as a signal in laterifstatements. This creates a clean separation between raw data access and user-facing formatting logic. At this point, your pricing module can create, modify, delete, and safely inspect dictionary entries.The next step builds on that foundation by turning lookup results into readable messages and by applying the same dictionary ideas to short text translation.
-
Challenge
Step 4: Format missing-data results and translate packing notes
This step shifts from raw data access to user-friendly behavior. A dictionary is most valuable when the rest of the application can turn its contents into useful output, whether that means a readable sentence or a translated text fragment. You will first convert campsite fee lookups into clear messages, then create a new dictionary that expands packing-note shortcuts, and finally apply that mapping across an entire line of text.
Formatting a lookup result is a small but important design skill. The data layer can expose simple values like integers or
None, while a presentation-oriented helper can convert those values into messages that make sense to a user. The same idea appears in web APIs, reporting systems, and template rendering. Text translation with dictionaries follows a similar principle: each token is looked up independently, and the program decides what to do when a token is unknown. In production systems, this pattern shows up in abbreviation expansion, code-to-label translation, log enrichment, and localization features.In this project,
app/fees.pywill handle message formatting for campsite prices, whileapp/packing.pywill own the token glossary and translation workflow. The CLI will later consume both kinds of output to build a cohesive daily briefing.Key terminology
- Formatter: A function that converts raw data into readable output.
- Token: One word or symbol in a string after splitting it into parts.
- Default value: A fallback returned when a dictionary key is missing.
What you'll accomplish
- Produce friendly fee lookup messages for found and missing site types.
- Define a packing shortcut glossary as a dictionary.
- Translate a note token by token while preserving unknown words.
--- ### Task 4.1 Context The pricing dictionary now returns raw integers or
None, but the CLI should not be responsible for deciding how those states are phrased for the user. A dedicated formatting helper keeps that presentation logic close to the lookup it depends on. This makes the final report easier to read and reduces duplication when multiple callers need the same wording. It is a common layering approach in applications that separate data access from display logic.Concepts
A conditional branch lets you react differently based on whether a lookup returned a real value or
None. In Python,Noneis falsey, which means it naturally fits intoif/elsechecks. The dictionarygetmethod is especially helpful here because it converts “missing key” into a manageable return value rather than an exception. Predictable output strings also make automated tests far easier to write and maintain. You have turned a missing-or-present lookup into readable text. The next task applies the same mapping mindset to a different domain by defining a dictionary of packing-note shortcuts.--- ### Task 4.2 Context The Trail Guide Helper also needs to expand short planning notes into something more readable. Before a note can be translated, the application needs a glossary-like mapping that defines what each shortcut means. Keeping that dictionary in its own function makes the translation logic independent from the actual data values. This is a practical design pattern whenever the same transformation rules need to be reused in multiple places.
Concepts
A glossary dictionary works just like any other mapping: the short token is the key, and the full phrase is the value. Because dictionary lookups are exact, casing matters unless you normalize input yourself. In many systems, token maps are kept separate from translation functions so the vocabulary can be extended without changing the algorithm. That is the approach you will use here. The glossary is ready.
Now you can loop through a note, translate the pieces that are known, and leave ordinary words untouched so the sentence still reads naturally.
--- ### Task 4.3 Context A full sentence or note is really just a sequence of smaller tokens. By translating one token at a time, the application can expand abbreviations without disturbing words that are already readable. This keeps the function flexible and avoids the need for complex parsing logic in a beginner-friendly project. The same approach is widely used in text preprocessing, command parsing, and lightweight normalization tasks.
Concepts
Splitting a string turns it into a list of tokens, usually by whitespace. Once you have the tokens, a loop can process each one independently. The pattern
dictionary.get(token, token)is especially useful because it returns the translated value when available and falls back to the original token when it is not. After processing,' '.join(...)combines the tokens back into a final string. You now have both user-friendly campsite messages and a working token translator.In the final step, you will package translated note data into a summary dictionary and compose all of the helpers into a finished CLI report.
-
Challenge
Step 5: Compose summaries and finalize the CLI report
This final step is about composition. Small functions become much more useful when they are combined into a single workflow that produces a meaningful result. You will first create a structured summary of a packing note, then assemble the full Trail Guide briefing by calling helpers from every module.
Composition is a key software design principle because it keeps each function narrow and understandable while still allowing the overall application to do something interesting. Rather than placing all logic in one large function, you build trustworthy pieces and orchestrate them from a higher-level layer. That makes debugging easier, encourages reuse, and keeps tests focused. In production systems, this approach appears everywhere: API controllers compose services, services compose repositories, and reporting jobs compose formatting and transformation helpers.
Here,
app/packing.pywill return a summary dictionary that groups related results under named keys. Thenapp/cli.pywill act as the coordinator. It will call the difficulty, fee, and packing helpers, apply a small sequence of dictionary mutations, and return a multi-line string that could be printed to the terminal. This is the point where the project shifts from isolated exercises to one coherent program.Key terminology:
- Composition: Building more complex behavior by combining smaller functions.
- Summary dictionary: A structured mapping that groups multiple related output values.
- Orchestration layer: Code that coordinates helpers from multiple modules.
What you'll accomplish:
- Return a summary dictionary containing the original note, translated note, and token count.
- Generate a final multi-line briefing that integrates all earlier dictionary operations.
- Finish with a working CLI entry point you can run from the terminal.
--- ### Task 5.1 Context The packing translator currently returns only a string, but the CLI needs a little more information than that. A summary helper can preserve the original note, include the translated note, and attach a count that may be useful for reporting or validation. Grouping those related values in a dictionary makes the result self-describing and easy to extend later. This is a practical pattern whenever a function needs to return more than one named piece of information.
Concepts
A summary dictionary is simply a mapping whose keys describe the meaning of each output value. This is often clearer than returning a tuple because callers do not have to remember positional ordering. Reusing
translate_packing_noteinside the summary helper is also an example of composition: one function delegates part of its job to another focused function. That reduces duplication and keeps the module easier to maintain. You now have a structured packing result.The last task brings every module together so the project ends as a complete, runnable application rather than a set of disconnected helper functions.
--- ### Task 5.2 Context The CLI module is the orchestration layer for the project. Its job is not to reinvent dictionary logic, but to call the helper functions in a sensible order and turn their results into one readable report. This separation mirrors real software structure, where data helpers stay small and a top-level service or command coordinates them. Finishing the CLI also gives you an integration point that proves the modules work together.
Concepts
Orchestration code composes lower-level functions into a workflow. In this case, the workflow includes creating dictionaries, updating and removing entries, checking for missing data, translating a note, and formatting the final output. Returning one multi-line string keeps the CLI predictable and testable because the
__main__block can simply print whatever the function returns. This pattern is ideal for small command-line utilities and scripted automation tasks.Wrap-up
By the end of this task, the Trail Guide Helper will be a complete example of beginner-friendly dictionary usage: lookups, updates, deletions, safe access, and translation all working together. As a next step, try extending the project with user input, JSON file loading, or additional categories of trail metadata so you can continue practicing dictionaries in slightly larger programs. ### Conclusion You finished the lab with a complete command-line application built around Python dictionaries.
Along the way, you:
- Created lookup tables
- Updated and deleted keys
- Handled missing values safely with
get - Translated tokens in a note
- Composed multiple helpers into one final report
A natural next exercise is to load the same kinds of mappings from JSON, accept user input from the terminal, or write additional pytest cases for custom scenarios.
About the author
Real skill practice before real-world application
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Learn by doing
Engage hands-on with the tools and technologies you’re learning. You pick the skill, we provide the credentials and environment.
Follow your guide
All labs have detailed instructions and objectives, guiding you through the learning process and ensuring you understand every step.
Turn time into mastery
On average, you retain 75% more of your learning if you take time to practice. Hands-on labs set you up for success to make those skills stick.