Skip to main content
PROGRAMMING / PYTHON / LESSON 05

Python Working with Data.

Turn raw values into useful IT information. Explore text, lists and dictionaries, organize inventory records, and build your first data-driven report.

Beginner → Intermediate75–95 minutesInteractive data explorerReal Python editor
00

What you'll learn

Control Flow taught us to process records. Now we'll understand the records themselves: how text is cleaned, how ordered collections work, and how named fields become the building blocks of inventory reports and future automation.

  • Distinguish strings, lists, dictionaries, tuples and sets by the problem they solve.
  • Access list items by index, slice safely and recognize mutable versus immutable values.
  • Read and update dictionary records using keys, get() and predictable missing-value defaults.
  • Transform and filter inventory records with loops without overwriting the original data.
  • Handle split(), strip(), join(), enumerate(), sorted() and basic type conversion.
  • Build a small report from structured device records and check edge cases.
  • Translate a familiar for loop into a short list comprehension, then decide which is easier to read.
  • Copy, sort, remove and update list or dictionary values without corrupting the source.
Our approach: Read each concept, predict the printed result, explore a fictional inventory, and only then write the code in Practice.
TEXT IS STRUCTURED DATA
01

Strings: inspect, clean and combine

An IP address, hostname or ticket message starts as text. Strings are sequences of characters: you can index or slice them, but not change one character in place. Normalize incoming text before comparing it. strip removes surrounding whitespace; lower creates a lowercase copy; split divides a string into a list using a delimiter. Keep the original when you need an audit trail.

Immutable values

name[0] reads a character, but name[0] = 'S' raises TypeError. Build a new string instead of trying to mutate one character.

Normalize explicitly

strip() and lower() return new strings. ' Offline '.strip().lower() equals 'offline', but the original text remains untouched.

PYTHON / PREDICT THE RESULTRead before you run
raw = '  SW-EDGE-01, Offline  '
parts = raw.strip().split(',')
host = parts[0].lower()
state = parts[1].strip().lower()
print(host, state)
Does split remove the whitespace after the comma automatically? Reveal explanation after you predict.

No. The second field initially contains a leading space. Applying strip() to that field removes it; the original string is unchanged.

Remember: Text cleaning is deliberate. Normalize every field you compare, not just the first one.
ORDERED AND CHANGEABLE
02

Lists: positions, slices and edits

A Python list holds an ordered collection of items. Lists are mutable, so append(), assignment and pop() modify the same object. Indices start at zero; -1 refers to the last element. A slice such as items[1:3] includes index 1 but excludes index 3. A shallow list slice copies its outer container, not nested objects.

Index safely

ports[0] is the first port; ports[-1] is last. Reading a missing index raises IndexError—check length when the position is uncertain.

Mutation is visible

append() modifies the list and returns None. Assign the original list to another name and both names refer to the same object unless you copy it.

PYTHON / PREDICT THE RESULTRead before you run
ports = [22, 80, 443]
ports.append(3389)
print(ports[0])
print(ports[1:3])
print(len(ports))
What does ports[1:3] print, and does the original list change? Reveal explanation after you predict.

It prints [80, 443]. The slice itself does not change ports, but append added 3389 to the original list, which now has four elements.

Remember: Choose a list for an ordered sequence you expect to grow or edit.
A LIST CAN CHANGE IN PLACE
03

Copy, sort and remove list entries deliberately

A list can change in place, unlike a string. Understand the difference between assigning another name to the same list and creating a shallow copy: alias = ports refers to the same list, while copy = ports.copy() creates an independent outer list. The sort() method changes a list and returns None; sorted(ports) builds a new sorted list. pop(index) removes and returns the entry at that index, so a careless pop can shift all later positions. Try these operations on fictional port lists before updating any real configuration.

Alias versus copy

alias = ports means both names point to the same list. copied = ports.copy() creates a separate outer container. Shallow copies still share nested objects: copying a list of dictionaries does not copy the dictionaries.

sort() versus sorted()

ports.sort() reorders ports and returns None. sorted(ports) leaves the original list in its existing order and produces a new sorted list. Choose based on whether input order must be retained.

pop(), remove() and membership

pop(1) removes and returns the item at index 1. remove(443) removes the first occurrence of value 443, raising ValueError when absent. Test membership or know the input contract before removing.

PYTHON / PREDICT THE RESULTRead before you run
ports = [443, 22, 3389, 80]
copy = ports.copy()
removed = copy.pop(2)
copy.sort()
print(ports)
print(copy)
print(removed)
Which list changes, what does removed contain, and what would print(copy.sort()) produce? Reveal explanation after you predict.

ports remains [443, 22, 3389, 80]. copy becomes [22, 80, 443], and removed is 3389. copy.sort() mutates and returns None, so printing that call would print None.

INTERACTIVE / COLLECTION MUTATION LAB

Does the original list change?

Choose an operation on a fictional port list and compare its before-and-after state. The actual Python methods are explained in the lesson; this explorer models their effects.

BEFOREports[443, 3389, 22, 80]
AFTER — ORIGINALports[443,3389,22,80]
AFTER — WORKING LISTclean[443,22,80]
clean = ports.copy()
clean.pop(1)

The copy has a separate outer list. Removing an item from clean does not change ports.

This example contains integers, so a shallow copy fully separates these outer list changes. Nested dictionaries would still be shared between shallow copies.

Remember: Choose copy() or sorted() when preserving the input matters. sort(), append() and pop() mutate a list, and sort() returns None.
NAMED FIELDS
04

Dictionaries: model a device record

A list tracks items by position; a dictionary tracks values by unique hashable keys. This is an excellent fit for inventory records with hostname, role and status. A missing key raises KeyError when accessed with brackets. Use get(key, default) when a field is optional and that default is explicitly meaningful; do not silently pretend missing evidence is healthy.

One record, many attributes

Use descriptive keys like 'hostname' and 'status'. Add or replace a field with record['status'] = 'online'.

Missing is not healthy

record.get('status', 'unknown') surfaces a missing status as unknown rather than inventing an online state.

PYTHON / PREDICT THE RESULTRead before you run
device = {'hostname': 'SW-01', 'status': 'offline'}
print(device['hostname'])
print(device.get('site', 'unknown'))
device['status'] = 'online'
print(device['status'])
What happens if you use device['site'] rather than get('site', 'unknown')? Reveal explanation after you predict.

Python raises KeyError because site does not exist in the dictionary. get provides the explicit fallback 'unknown'.

Remember: Use dictionaries for named fields; make optional or missing values obvious.
ADD, UPDATE AND REMOVE NAMED FIELDS
05

Dictionary updates, iteration and safe removal

A dictionary stores keys and values rather than numeric positions. You can add a new field with record['site'] = 'HQ', replace a value with update(), and remove a key with pop(key, default) or del. Without a default, pop raises KeyError if the key does not exist. When reading a dictionary, items() yields key/value pairs and keys() yields keys; iteration over a dictionary directly also visits its keys. Copies are shallow, just like list copies, so protect the original inventory when reporting.

Updates and copies

new_record = record.copy() makes a new outer dictionary. new_record.update({'status':'offline'}) changes the copy, not the original. Nested lists or dictionaries remain shared unless copied at deeper levels.

Safe deletion

record.pop('retired_field', None) removes the key when present and avoids KeyError when absent. Use del record['field'] only if the key must exist.

Iterate keys and values

for key, value in record.items(): visits each pair. Use get(key, default) for optional fields; the default should represent unknown rather than assuming a healthy value.

PYTHON / PREDICT THE RESULTRead before you run
original = {'host': 'SW-01', 'status': 'online', 'legacy': True}
review = original.copy()
review.update({'status': 'offline', 'site': 'HQ'})
review.pop('legacy', None)
print(original['status'])
print(sorted(review.items()))
Does original['status'] change? Which fields remain in the new review copy? Reveal explanation after you predict.

original['status'] is still online. The new copy contains host SW-01, site HQ and status offline. legacy was removed only from the copied record.

Remember: Copy before editing when source records must remain unchanged; update adds or replaces, and pop(key, default) safely removes optional keys.
INVENTORIES ARE COLLECTIONS OF RECORDS
06

Combine lists and dictionaries

An IT inventory often consists of a list of dictionaries. Each dictionary describes one device and the list holds all the records. Loop through those records and access fields by name. When data is inconsistent, normalize status carefully. The sample is fictional and does not connect to live systems.

Separate each record

Each device is its own dictionary within one list, making hostnames and statuses easy to associate.

Loop through records

for device in devices lets you read each device['hostname']. Use device.get('status', 'unknown') to avoid assuming all sources are complete.

PYTHON / PREDICT THE RESULTRead before you run
devices = [
    {'hostname': 'SW-01', 'status': 'online'},
    {'hostname': 'AP-02', 'status': 'offline'},
]
for device in devices:
    print(device['hostname'], device['status'])
How many dictionary records does this list hold, and how many lines print? Reveal explanation after you predict.

Two records and two printed lines. The outer list provides ordering, and each dictionary provides meaningful field names.

INTERACTIVE DATA EXPLORER / FICTIONAL INVENTORY

Change records. Watch the report.

Try a filter, toggle sorting, or change a device's status. This is a simulation of how a list of dictionaries is processed—not a real network connection.

HostnameSiteStatus (editable)
SW-01HQ
AP-02BR1
FW-01HQ
SRV-01BR2
4 matching records · 2 offline in source
[
  {
    "host": "AP-02",
    "site": "BR1",
    "status": "offline"
  },
  {
    "host": "FW-01",
    "site": "HQ",
    "status": "offline"
  },
  {
    "host": "SRV-01",
    "site": "BR2",
    "status": "unknown"
  },
  {
    "host": "SW-01",
    "site": "HQ",
    "status": "online"
  }
]

The filtered output is a new collection. Changing the filter does not delete or rewrite the source records.

Remember: A list of dictionaries is a flexible bridge from beginner Python to CSV and API data.
TURN RECORDS INTO ANSWERS
07

Filter and count matching records

Real operations reports answer questions such as which devices are offline. Begin with an empty results list, inspect every record, append matches, and report after the loop. Avoid accidentally assigning the original list to your results; this would make your edits affect the input. Normalize status for case and whitespace differences before comparing.

Keep the source intact

Build a fresh offline_hosts list rather than deleting records while iterating.

Report exact matches

Normalize with str(...).strip().lower() when the source format warrants it. Distinguish 'unknown' from 'offline'.

PYTHON / PREDICT THE RESULTRead before you run
devices = [
    {'host': 'SW-01', 'status': 'Online'},
    {'host': 'AP-02', 'status': ' offline '},
]
offline_hosts = []
for device in devices:
    if device['status'].strip().lower() == 'offline':
        offline_hosts.append(device['host'])
print(offline_hosts)
Why doesn't the first device appear in the final results? Reveal explanation after you predict.

Its normalized status is online rather than offline. Only AP-02 matches the condition and gets appended to the new list.

Remember: Filter into a new collection, then summarize. Do not overwrite the source accidentally.
THE RIGHT COLLECTION FOR THE JOB
08

Tuples and sets: fixed groups and uniqueness

Tuples are ordered immutable sequences; they are useful for values you want to keep grouped without changing their positions. Sets store unique hashable elements and support membership checks, but they do not preserve an indexable sequence. Converting a list to a set removes duplicates, and printing a set is not guaranteed to use a stable order. Sort for deterministic output.

Tuple means fixed structure

('SW-01', 22) can be unpacked into hostname, port. You cannot append to a tuple, though nested mutable objects can still change.

Set means unique members

Use set() to count distinct sites. Sort before displaying values when a stable user-facing report matters.

PYTHON / PREDICT THE RESULTRead before you run
sites = ['HQ', 'BR1', 'HQ', 'BR2']
unique_sites = set(sites)
print(len(unique_sites))
print(sorted(unique_sites))
Will set(sites) contain two copies of HQ? Reveal explanation after you predict.

No. Sets contain unique members, so this example has three distinct sites. sorted() produces the predictable order ['BR1', 'BR2', 'HQ'].

Remember: Tuples group fixed positions; sets answer uniqueness and membership questions.
A SHORTER WAY TO BUILD A NEW LIST
09

List comprehensions: understand the long way first

You already know how to start with an empty list, loop over records, test a condition and append matches. A list comprehension is a shorter expression for that same common pattern. Read the version with a for loop first, then compare it with [expression for item in collection if condition]. Both produce a new list without removing the original records. You do not have to prefer the shorter syntax if the expanded loop is easier to debug.

Left side: what to collect

In [device['host'] for device in devices if ...], device['host'] is the value stored in the new list. It is not an index into the resulting list.

Right side: repeat and filter

for device in devices visits each record, while if device['status'] == 'offline' selects which ones are included. Leave the if out when every item belongs in the result.

PYTHON / PREDICT THE RESULTRead before you run
devices = [{'host':'SW-01','status':'online'},
           {'host':'AP-02','status':'offline'}]
slow_way = []
for device in devices:
    if device['status'] == 'offline':
        slow_way.append(device['host'])
short_way = [device['host'] for device in devices
             if device['status'] == 'offline']
print(slow_way == short_way)
print(short_way)
Do both methods create the same new list? What would happen if no records were offline? Reveal explanation after you predict.

Yes. Both create ['AP-02'], and the comparison prints True. If no record matched, each approach would produce an empty list rather than an error. Use the expanded loop when it communicates the steps more clearly.

Remember: A list comprehension is a compact for-and-optional-if expression that builds a list. Understand the loop form before using the shortcut.
CLEAN INPUTS, CLEAR OUTPUTS
10

Transform, sort and format

An inventory may arrive as a delimited text line. split() separates fields; strip() cleans them; int() converts a numeric-looking string to an integer and can raise ValueError for bad input. enumerate() supplies a counter plus the current item as you loop, usually starting at zero. For example, for position, hostname in enumerate(['SW-01', 'AP-02'], start=1): prints positions 1 and 2 when you print(position, hostname). sorted() returns a sorted new list. join() combines a sequence of strings into a readable report; nonstrings must be converted first.

Conversions can fail

int('443') becomes 443, but int('443/tcp') raises ValueError. Validate formats and add error handling as a later skill.

Output contract matters

Use sorted(hosts) for a consistent report and ', '.join(hosts) to combine known strings without accidental list-representation formatting.

PYTHON / PREDICT THE RESULTRead before you run
raw = 'SW-01,443,online'
name, port_text, status = [part.strip() for part in raw.split(',')]
port = int(port_text)
print(f'{name}: {port} / {status}')
Which operation turns '443' from text into a number? Reveal explanation after you predict.

int(port_text) converts the string '443' to integer 443. This conversion can fail for malformed input, so input validation matters.

Remember: Data transformation involves both structure and types; confirm formats before converting.
PUT IT TOGETHER
11

Create a small device health report

A useful first automation reads a collection, classifies its records, and prints a reliable summary. Keep the original inventory available, store intermediate results separately, then report counts and hostnames. Test zero failures, several failures and missing or malformed statuses. This prepares you for CSV, JSON, file handling and API requests later in the Python path.

Inspect before changing

Count records with len(), then filter. Print the final results outside the loop to avoid premature or repeated summaries.

Design for missing fields

get('status', 'unknown') avoids a crash but also keeps unknown distinct from offline. Never count unknown as healthy by default.

PYTHON / PREDICT THE RESULTRead before you run
inventory = [
    {'host': 'SW-01', 'status': 'online'},
    {'host': 'AP-02', 'status': 'offline'},
    {'host': 'FW-01', 'status': 'offline'},
]
offline = [d['host'] for d in inventory if d.get('status') == 'offline']
print(f"{len(offline)} offline: {', '.join(sorted(offline))}")
What should the report say and why should sorted be used? Reveal explanation after you predict.

It should say '2 offline: AP-02, FW-01'. Sorting makes the report deterministic if the input order changes.

Remember: A meaningful IT report combines a collection, selection logic and a stable output format.
12 / APPLY IT YOURSELF
12

Make a real data transformation

Use the same real in-browser Python environment from earlier lessons. Start by normalizing a messy status string, then continue to eight more data-handling challenges, including copying, sorting, dictionary updates and tuple unpacking, in Practice.

JOELEARNS / PYTHON WORKBENCH

Write it. Run it. Understand it.

Clean, combine and report on real Python collections. Write a transformation and verify it.

Python 3.14 · in-browser
CHALLENGE 01 / 9 · BEGINNER

Normalize a device status

The fictitious inventory returned a whitespace-padded mixed-case status. Normalize it to lowercase 'offline', store the result in clean_status and print exactly 'offline'. Do not replace the raw value itself.

EXPECTED OUTPUToffline
script.py editableCtrl / Cmd + Enter to run
Your code is stored only in session storage when enabled. Never paste secrets. Clearing this option clears saved drafts, not the editor.
Code executes only when you choose Run or Check. No account needed.
HINTS · OPTIONAL

Try solving it before asking for help. Hints never show a completed program.

PYTHON OUTPUTReady

Your printed output and errors will appear here.

VARIABLE TRACER

Run the code, then step through the executed lines to watch variable values change.

About this environment: The Python runtime is loaded on demand from a version-pinned CDN and runs inside a sandboxed, opaque-origin browser frame. Runs have a time limit. OS/network automation is outside this introductory workbench. If your code calls input(), a response panel appears to supply text to those calls. Don't paste secrets or untrusted scripts.

Next required step: complete the 9-challenge Practice milestone →
NEXT STEP

From reading collections to processing them.

Practice cleaning strings, editing lists, handling missing dictionary keys, filtering records, finding unique sites and building a small health report.

Continue to all Working with Data challenges →
13

Reference material

Python's official tutorial explains these types in greater detail, including behaviors you'll revisit during file handling and automation.

NEXT REQUIRED STEP · PYTHON FUNDAMENTALS

Complete the Practice milestone

You've reached the end of this written lesson. Your next step is practice—complete Python Working with Data Coding Challenges before proceeding to a new lesson. This is milestone 10 of 18. You can revisit the explanation whenever you need it.