Session 3 — Chapter 5: Dictionaries · Chapter 6: User Input and While Loops
SEC1302 · Python for Information Security
>>> service = {"name": "ssh", "port": 22, "state": "open"}
>>> while service["state"] == "open":
... print(f"Monitoring port {service['port']}")
... break
Monitoring port 225.1 A Simple Dictionary Example
5.2 Working with Dictionaries
5.3 Looping Through a Dictionary
5.4 Nesting
6.1 How the input() Function Works
6.2 Introducing while Loops
6.3 Using while Loops with Lists and Dictionaries
Exercises & Session Synthesis
Perform key-based lookups, insertions, updates, deletions, and defensive retrieval with get().
Traverse keys, values, and items; apply sorting and set deduplication to dictionary components.
Model structured data using lists of dictionaries, lists within dictionaries, and hierarchical dictionaries (JSON / REST API format).
Implement sentinel quits, boolean flags, break/continue flow controls, and queue processing.
Mapped to CLO1 — apply Python data structures and iterative control flows to program technical utilities (C3-P2).
A dictionary in Python is an unordered collection of key-value pairs (ordered by insertion since Python 3.7).
Each key is connected to a value; a key can be used to retrieve, modify, or delete its associated value.
Written with curly braces { }, with key-value pairs separated by commas, and keys separated from values by colons.
alien_0 = {'color': 'green', 'points': 5}
print(alien_0){'color': 'green', 'points': 5}To access a value, write the dictionary name followed by the key in square brackets: dict[key].
Python searches the hash table and returns the associated value directly.
alien_0 = {'color': 'green', 'points': 5}
print(alien_0['color'])
new_points = alien_0['points']
print(f"You just earned {new_points} points!")green
You just earned 5 points!Dictionaries are dynamic structures: new key-value pairs can be added at any time.
Syntax: dict[new_key] = new_value.
New entries are appended to the dictionary's internal mapping, preserving insertion order.
alien_0 = {'color': 'green', 'points': 5}
print(alien_0)
alien_0['x_position'] = 0
alien_0['y_position'] = 25
print(alien_0){'color': 'green', 'points': 5}
{'color': 'green', 'points': 5', 'x_position': 0, 'y_position': 25}It is standard practice to define an empty dictionary using empty curly braces {} or dict() and populate it as runtime data arrives.
Particularly common when parsing network packets, ingesting JSON configuration files, or harvesting user input.
alien_0 = {}
alien_0['color'] = 'green'
alien_0['points'] = 5
print(alien_0){'color': 'green', 'points': 5}To modify a value, assign a new value to an existing key: dict[existing_key] = new_value.
Enables state tracking in monitoring systems, game engines, and network telemetry.
alien_0 = {'x_position': 0, 'y_position': 25, 'speed': 'medium'}
print(f"Original position: {alien_0['x_position']}")
# Move alien to the right based on current speed
if alien_0['speed'] == 'slow':
x_increment = 1
elif alien_0['speed'] == 'medium':
x_increment = 2
else:
x_increment = 3
alien_0['x_position'] = alien_0['x_position'] + x_increment
print(f"New position: {alien_0['x_position']}")Original position: 0
New position: 2When a piece of information is no longer needed, use the del statement with the key: del dict[key].
The deletion removes both the key and its associated value permanently.
alien_0 = {'color': 'green', 'points': 5}
print(alien_0)
del alien_0['points']
print(alien_0){'color': 'green', 'points': 5}
{'color': 'green'}While a dictionary can store multiple attributes of one object (an alien), it can also store one attribute across many objects (favorite programming languages of team members).
Multi-line formatting: PEP 8 recommends placing each key-value pair on its own indented line, with a closing brace on a new line matching the indentation.
favorite_languages = {
'jen': 'python',
'sarah': 'c',
'edward': 'ruby',
'phil': 'python',
}
language = favorite_languages['sarah'].title()
print(f"Sarah's favorite language is {language}.")Sarah's favorite language is C.Using square bracket notation dict[key] on an absent key terminates execution with a KeyError.
The get() method accepts a key and an optional default fallback value: dict.get(key, default).
If the key exists, it returns the corresponding value; if absent, it returns the default (or None if omitted).
alien_0 = {'color': 'green', 'speed': 'slow'}
point_value = alien_0.get('points', 'No point value assigned.')
print(point_value)
missing_value = alien_0.get('points')
print(missing_value)No point value assigned.
NonePython dictionaries are implemented using Hash Tables.
When an entry is stored, Python passes the key through the built-in hash() function to compute an integer hash code, which determines the bucket index.
Lookup complexity: O(1) average time complexity for insertion, retrieval, and deletion.
Keys MUST be immutable (hashable): strings, integers, floats, and tuples containing only immutables.
Lists and dictionaries CANNOT be keys (TypeError: unhashable type: 'list').
print(hash("admin")) # Returns integer hash
# invalid_dict = {[1, 2]: "forbidden"}
# Raises TypeError: unhashable type: 'list'Flowchart for algorithm to add element to the hash table of a set

Only ONE table. Each slot stores a triple: [hash_code, key_pointer, value_pointer]. With a table size of 8, 5 slots stay empty (NULL).
Example: d = {}; d['timmy'] = 'red'; d['barry'] = 'green'; d['guido'] = 'blue' with hash('timmy') % 8 = 4, hash('barry') % 8 = 1, hash('guido') % 8 = 0.
The data is split into TWO separate arrays: a dense entries array (only the 3 real items, stored in insertion order) and a sparse indices table (small integers pointing to rows in entries, stored as 1-byte int8_t values).
Indices table (8 slots, 1 byte each)
Entries array (dense, in insertion order)
Source: Internals of sets and dicts
Before Python 3.6: Dictionaries stored hash, key, and value in a single sparse array, wasting substantial memory (dense table with empty slots).
Python 3.6+ (PEP 468 / Raymond Hettinger design): Split into a compact entries array and a small indices array.
"Dictionaries preserve insertion order. Note that updating a key does not affect the order."
The items() method returns a sequence-like view of key-value tuples: [(k1, v1), (k2, v2), ...].
Use tuple unpacking in a for loop: for key, value in dictionary.items():.
user_0 = {
'username': 'efermi',
'first': 'enrico',
'last': 'fermi',
}
for key, value in user_0.items():
print(f"Key: {key}")
print(f"Value: {value} ")Key: username
Value: efermi
Key: first
Value: enrico
Key: last
Value: fermiThe keys() method returns a view of all keys in the dictionary.
Looping through a dictionary directly (for name in dictionary:) defaults to iterating through keys. Explicitly calling .keys() clarifies programmer intent.
favorite_languages = {
'jen': 'python',
'sarah': 'c',
'edward': 'ruby',
'phil': 'python',
}
friends = ['phil', 'sarah']
for name in favorite_languages.keys():
print(f"Hi {name.title()}.")
if name in friends:
language = favorite_languages[name].title()
print(f" {name.title()}, I see you love {language}!")Hi Jen.
Hi Sarah.
Sarah, I see you love C!
Hi Edward.
Hi Phil.
Phil, I see you love Python!A dictionary preserves insertion order, but analytical tasks often require alphabetical or numerical order.
Wrap the keys() method with the sorted() function: for key in sorted(dictionary.keys()):.
favorite_languages = {
'jen': 'python',
'sarah': 'c',
'edward': 'ruby',
'phil': 'python',
}
for name in sorted(favorite_languages.keys()):
print(f"{name.title()}, thank you for taking the poll.")Edward, thank you for taking the poll.
Jen, thank you for taking the poll.
Phil, thank you for taking the poll.
Sarah, thank you for taking the poll.To access values without regard to their associated keys, use the values() method.
Returns a dict_values view containing all values, including duplicates.
favorite_languages = {
'jen': 'python',
'sarah': 'c',
'edward': 'ruby',
'phil': 'python',
}
print("The following languages have been mentioned:")
for language in favorite_languages.values():
print(language.title())The following languages have been mentioned:
Python
C
Ruby
PythonTo eliminate duplicate values from a collection, wrap the values() call with the set() function.
A set is an unordered collection in which every element must be unique.
Set literals can also be written using curly braces without colons: {'python', 'c', 'ruby'}.
favorite_languages = {
'jen': 'python',
'sarah': 'c',
'edward': 'ruby',
'phil': 'python',
}
print("Unique languages mentioned:")
for language in set(favorite_languages.values()):
print(language.title())Unique languages mentioned:
Ruby
C
PythonDictionary: {'key': 'value'} → key-value pairs
Set: {'value1', 'value2'} → collection of unique elements
Nesting refers to storing a list inside a dictionary, a dictionary inside a list, or a dictionary inside another dictionary.
Mimics hierarchical data standards in modern computing: JSON (JavaScript Object Notation) used in REST APIs and web services; YAML configuration files in Kubernetes and cloud deployments; NoSQL document databases like MongoDB.
List of Dictionaries
[ {user1}, {user2}, {user3} ] → Tabular records / fleets.
List in a Dictionary
{ user: ['role1', 'role2'] } → One-to-many relationships.
Dictionary in a Dictionary
{ user1: {profile}, user2: {profile} } → Hierarchical entities.
Storing multiple individual entities (represented as dictionaries) inside a single list.
Enables batch processing, scaling fleets, and querying database rows.
aliens = []
# Create a fleet of 3 green aliens
for alien_number in range(3):
new_alien = {'color': 'green', 'points': 5, 'speed': 'slow'}
aliens.append(new_alien)
# Modify the first alien in the fleet
aliens[0]['color'] = 'yellow'
aliens[0]['speed'] = 'medium'
for alien in aliens:
print(alien){'color': 'yellow', 'points': 5, 'speed': 'medium'}
{'color': 'green', 'points': 5, 'speed': 'slow'}
{'color': 'green', 'points': 5, 'speed': 'slow'}Storing a list as a dictionary value allows one key to be associated with multiple items.
Example: an order with a list of toppings, or a network firewall host with a list of open ports.
pizza = {
'crust': 'thick',
'toppings': ['mushrooms', 'extra cheese'],
}
print(f"You ordered a {pizza['crust']}-crust pizza with:")
for topping in pizza['toppings']:
print(f" - {topping}")You ordered a thick-crust pizza with:
- mushrooms
- extra cheeseserver = {
'hostname': 'auth-01',
'open_ports': [22, 443, 8443]
}Storing a dictionary as the value of another dictionary enables nested namespace structuring.
Common in representing user profiles, configuration trees, and multi-tenant environments.
users = {
'aeinstein': {
'first': 'albert',
'last': 'einstein',
'location': 'princeton',
},
'mcurie': {
'first': 'marie',
'last': 'curie',
'location': 'paris',
},
}
for username, user_info in users.items():
print(f" Username: {username}")
full_name = f"{user_info['first']} {user_info['last']}"
print(f" Full name: {full_name.title()}")
print(f" Location: {user_info['location'].title()}")Username: aeinstein
Full name: Albert Einstein
Location: Princeton
Username: mcurie
Full name: Marie Curie
Location: ParisDecoded JWT headers and payloads are Python dictionaries containing standard claims (iss, sub, exp, roles).
Web frameworks (Flask, FastAPI, Django) parse client requests into dictionaries: request.headers['Authorization'].
Syslog, Zeek, and Suricata JSON event logs are ingested directly into dictionaries for fast rule-based field inspection.
National Vulnerability Database feeds are structured nested dictionaries containing CVE IDs, CVSS scores, and affected CPE ranges.
Standards Source: RFC 7519 — JSON Web Token (JWT): datatracker.ietf.org/doc/html/rfc7519 · NIST NVD JSON API Schema: nvd.nist.gov/developers/vulnerabilities
The input() function pauses program execution and waits for the user to type text in the console and press Enter.
The entered text is captured and returned as a string (str).
It takes one argument: the prompt string displayed to the user.
message = input("Tell me something, and I will repeat it back to you: ")
print(message)Tell me something, and I will repeat it back to you: Hello everyone!
Hello everyone!input() ALWAYS returns a string (str), even if the user types numeric digits (e.g. typing 21 returns "21", not 21).
Prompts must clearly instruct the user what kind of information is expected.
Add a space at the end of the prompt string to separate the prompt text from the user's cursor.
When instructions exceed one line, build a multi-line prompt string across multiple lines using +=.
prompt = "If you share your name, we can personalize the messages you see."
prompt += " What is your first name? "
name = input(prompt)
print(f" Hello, {name}!")If you share your name, we can personalize the messages you see.
What is your first name? Eric
Hello, Eric!Clean prompts reduce user entry errors and clarify interface flow.
Because input() yields a string, passing it directly to a numerical comparison (<, >) raises TypeError: '<' not supported between instances of 'str' and 'int'.
Use the int() function to convert the input string to an integer, or float() for floating-point decimals.
age = input("How old are you? ")
age = int(age)
if age >= 18:
print("You are old enough to vote!")
else:
print("You are not old enough to vote yet.")How old are you? 21
You are old enough to vote!If the user inputs non-digit characters (e.g. "twenty"), int() raises ValueError: invalid literal for int() with base 10.
The modulo operator % divides one number by another and returns the remainder (e.g., 7 % 3 is 1).
It does not return the quotient; it returns what remains after integer division.
Core use-case: determining whether an integer is even (number % 2 == 0) or odd (number % 2 != 0).
number = input("Enter a number, and I'll tell you if it's even or odd: ")
number = int(number)
if number % 2 == 0:
print(f" The number {number} is even.")
else:
print(f" The number {number} is odd.")Enter a number, and I'll tell you if it's even or odd: 42
The number 42 is even.Modulo arithmetic is the mathematical foundation of public-key cryptography (RSA, Diffie-Hellman).
A for loop takes a collection of items and executes a code block once for each item in the collection (definite iteration).
A while loop runs as long as (while) a specific condition evaluates to True (indefinite iteration).
while condition:
# Code block executed repeatedly while condition is TrueA while loop must modify variables involved in the condition to ensure the condition eventually becomes False.
current_number = 1
while current_number <= 5:
print(current_number)
current_number += 11
2
3
4
5Define a sentinel value (such as 'quit') that signals the loop to stop.
Initialize the control variable with a non-sentinel value so the loop can start.
prompt = " Tell me something, and I will repeat it back to you:"
prompt += " Enter 'quit' to end the program. "
message = ""
while message != 'quit':
message = input(prompt)
if message != 'quit':
print(message)Tell me something, and I will repeat it back to you:
Enter 'quit' to end the program. Hello!
Hello!
Tell me something, and I will repeat it back to you:
Enter 'quit' to end the program. quitWhen many different events or conditions could cause a program to stop running, managing a single condition in the while statement becomes awkward.
A flag variable acts as a master switch: while active:. If any event requires the program to stop, set active = False.
prompt = " Enter 'quit' to exit: "
active = True
while active:
message = input(prompt)
if message == 'quit':
active = False
elif message == 'lockout':
print("Security alert: terminal locked.")
active = False
else:
print(f"Echo: {message}")Enter 'quit' to exit: status
Echo: status
Enter 'quit' to exit: lockout
Security alert: terminal locked.The break statement directs Python to immediately exit the while loop or for loop without running any remaining code in the loop.
It bypasses the conditional test and ends the loop instantly.
prompt = " Please enter the name of a city you have visited:"
prompt += " (Enter 'quit' when you are finished.) "
while True:
city = input(prompt)
if city == 'quit':
break
else:
print(f"I'd love to go to {city.title()}!")Please enter the name of a city you have visited:
(Enter 'quit' when you are finished.) tokyo
I'd love to go to Tokyo!
Please enter the name of a city you have visited:
(Enter 'quit' when you are finished.) quitThe continue statement tells Python to stop executing the current iteration and return to the beginning of the loop to re-evaluate the condition.
Useful for skipping invalid inputs, malformed records, or comments in configuration files without terminating the entire loop.
current_number = 0
while current_number < 10:
current_number += 1
if current_number % 2 == 0:
continue
print(current_number)1
3
5
7
9[ Check number % 2 == 0 ]
(True) → [ continue ] → (Jumps directly to while current_number < 10)
(False) → [ print(current_number) ]
Every while loop must have a reachable path to make its conditional test False or execute a break statement.
If the variable controlling the loop is never updated, the loop runs endlessly until memory or CPU exhaustion occurs.
x = 1
while x <= 5:
print(x)
# Bug: x += 1 was omitted! x stays 1 forever.max_retries = 5
while attempts < max_retries:
...Terminal command to abort an infinite loop: Ctrl + C (sends SIGINT signal).
Modifying a list with for loops while iterating over it causes Python to lose track of index positions.
Use a while loop with pop() to remove items from an unverified list and append() them to a confirmed list until the work queue is empty.
unconfirmed_users = ['alice', 'brian', 'candace']
confirmed_users = []
while unconfirmed_users:
current_user = unconfirmed_users.pop()
print(f"Verifying user: {current_user.title()}")
confirmed_users.append(current_user)
print(" The following users have been confirmed:")
for confirmed_user in confirmed_users:
print(confirmed_user.title())Verifying user: Candace
Verifying user: Brian
Verifying user: Alice
The following users have been confirmed:
Candace
Brian
AliceThe remove() method removes only the FIRST occurrence of a value.
To purge every occurrence of a specific value (e.g. blacklisted IP, spam flag), combine while and in with remove().
pets = ['dog', 'cat', 'dog', 'goldfish', 'cat', 'rabbit', 'cat']
print(f"Original: {pets}")
while 'cat' in pets:
pets.remove('cat')
print(f"Filtered: {pets}")Original: ['dog', 'cat', 'dog', 'goldfish', 'cat', 'rabbit', 'cat']
Filtered: ['dog', 'dog', 'goldfish', 'rabbit']Cleansing raw packet traces to strip out internal broadcast addresses before exporting external telemetry.
Combining input() and while loops allows interactive population of key-value dictionaries.
Prompt for a key, prompt for a value, assign responses[key] = value, and prompt whether to continue.
responses = {}
polling_active = True
while polling_active:
name = input(" What is your name? ")
response = input("Which mountain would you like to climb someday? ")
responses[name] = response
repeat = input("Would you like to let another person respond? (yes/ no) ")
if repeat == 'no':
polling_active = False
print(" --- Poll Results ---")
for name, response in responses.items():
print(f"{name} would like to climb {response}.")What is your name? Alice
Which mountain would you like to climb someday? Denali
Would you like to let another person respond? (yes/ no) no
--- Poll Results ---
Alice would like to climb Denali.Never trust user input: users may provide malformed strings, malicious payloads, or leading/trailing whitespace.
Use string sanitization methods: .strip(), .lower(), .replace().
raw_port = input("Enter port number: ").strip()
if not raw_port.isdigit():
print("Error: Port must contain only numeric digits.")
else:
port = int(raw_port)
if 1 <= port <= 65535:
print(f"Valid port configured: {port}")
else:
print("Error: Port out of valid range (1 - 65535).")
Common Weakness Enumeration CWE-400: Resource Exhaustion.
When a while loop condition relies on external network data or user input without an upper bound, attackers can trigger infinite loops or extreme CPU consumption (Denial of Service).
# while packet_received: process(packet)
# If flood attack, thread hangs indefinitelyimport time
MAX_ATTEMPTS = 100
TIMEOUT_SECONDS = 5.0
start_time = time.time()
attempts = 0
while attempts < MAX_ATTEMPTS:
if time.time() - start_time > TIMEOUT_SECONDS:
print("Safety timeout reached. Aborting loop.")
break
attempts += 1Security Standard: MITRE CWE-400: cwe.mitre.org/data/definitions/400.html · OWASP Denial of Service Cheat Sheet: cheatsheetseries.owasp.org
In production operating systems and cloud services, network servers run inside continuous while True event loops: Web servers (Nginx, Gunicorn, Uvicorn) listen on sockets indefinitely; Python asyncio event loop processes concurrent I/O tasks.
# Minimal server listener skeleton:
# while True:
# client_sock, addr = server_sock.accept()
# handle_client(client_sock)Demonstrating how dictionaries and while loops interact in authentication subsystems: Tracking login attempts per IP/username, locking out after threshold K consecutive failures.
user_db = {'admin': 'SecretPass123!'}
failed_attempts = 0
MAX_FAILS = 3
while failed_attempts < MAX_FAILS:
pwd = input("Enter password: ")
if pwd == user_db['admin']:
print("Authentication successful! Welcome.")
failed_attempts = 0
break
else:
failed_attempts += 1
remaining = MAX_FAILS - failed_attempts
print(f"Invalid password. Attempts remaining: {remaining}")
else:
print("ALERT: Account locked due to excessive failed attempts.")NIST SP 800-63B (Digital Identity Guidelines: Authentication and Lifecycle Management)
Architecture: Combining nested dictionaries, user input, input validation, and while loop queue processing.
inventory = {}
while True:
service_name = input("Enter service name (or 'quit' to finalize): ").strip().lower()
if service_name == 'quit':
break
port_str = input(f"Enter port for {service_name}: ").strip()
if not port_str.isdigit():
print("Port must be numeric. Skipping entry.")
continue
port = int(port_str)
protocol = input("Enter protocol (TCP/UDP): ").strip().upper()
inventory[service_name] = {'port': port, 'protocol': protocol}
print(" --- Verified Service Inventory ---")
for svc, data in sorted(inventory.items()):
print(f"Service: {svc.upper():<10} | Port: {data['port']:<5} | Protocol: {data['protocol']}")--- Verified Service Inventory ---
Service: HTTPS | Port: 443 | Protocol: TCP
Service: SSH | Port: 22 | Protocol: TCPCourse: SEC1302 · Python for Information Security · Primary Textbook: Eric Matthes, Python Crash Course, 2nd ed.
Dictionaries