Lab 1: The “Memory Address” Detective Prove that Strings are immutable.
text = "DevOps"
print(f"Original ID: {id(text)}")
text = text + " Guru"
print(f"New ID: {id(text)}")
# Result: IDs are different. Python created a new string object.
Lab 2: The “Tuple Trap” (The Loophole) Try to hack a tuple.
# A Tuple containing a List
secure_config = ("127.0.0.1", [80, 443])
# secure_config[0] = "10.0.0.1" # Error: Tuple allows no change.
# BUT, we can change the list inside!
secure_config[1].append(8080)
print(secure_config)
# Output: ('127.0.0.1', [80, 443, 8080]) -> Modified!
Lab 3: Safe Type Casting Convert User Input (String) to Integer safely.
user_input = " 500 " # Note the spaces
try:
# 1. Strip spaces, 2. Convert
value = int(user_input.strip())
print(f"Processing amount: {value}")
except ValueError:
print("Error: Please enter a valid number.")Lab 4: The “Performance Bottleneck” (String Concatenation)
Scenario: You are writing a script to generate a massive report string from thousands of log lines.
- The “Bad” Way: Using
+=on a string. Since strings are Immutable, Python has to destroy and recreate the entire string variable for every single line. This kills the CPU. - The “Architect” Way: Use a List (Mutable) to collect parts, then join them once.
import time
# Simulation: 100,000 log lines
log_lines = ["Error: Connection timeout" for _ in range(100000)]
# --- METHOD 1: The Slow Way (Immutable Concatenation) ---
start_time = time.time()
report = ""
for line in log_lines:
report += line + "\n" # DANGER: Creates a NEW object 100,000 times!
print(f"Bad Method took: {time.time() - start_time:.4f} seconds")
# --- METHOD 2: The Fast Way (Mutable List Buffer) ---
start_time = time.time()
buffer_list = []
for line in log_lines:
buffer_list.append(line) # Fast! Modifies list in-place.
final_report = "\n".join(buffer_list) # Creates string ONCE at the end.
print(f"Architect Method took: {time.time() - start_time:.4f} seconds")
Expected Output: You will see the “Architect Method” is significantly faster (often 100x faster for large datasets).
Lab 5: The “Unhashable Key” Crash
Scenario: You want to create a dictionary that maps a specific set of Open Ports (e.g., [80, 443]) to a Security Group Name.
- The Rule: Dictionary keys must be Immutable (Hashable).
- The Error: Using a List as a key triggers a
TypeError.
# Goal: Map a list of ports to a Profile Name
# key: [80, 443] -> value: "Web-Server-Profile"
try:
# ATTEMPT 1: Using a List (Mutable) as a Key
port_list = [80, 443]
security_map = {
port_list: "Web-Server-Profile"
}
except TypeError as e:
print(f"CRASH: {e}")
# Output: unhashable type: 'list'
# ATTEMPT 2: The Fix - Cast to Tuple (Immutable)
port_tuple = tuple([80, 443]) # Explicit Casting
security_map = {
port_tuple: "Web-Server-Profile"
}
print(f"Success! Profile mapped: {security_map[port_tuple]}")
- Architect Insight: This happens constantly when parsing JSON. If you need to use a complex object as a unique identifier (key), always convert it to a
tupleorfrozensetfirst.
Lab 6: The “Input Sanitizer” (Casting & Cleaning)
Scenario: You are reading data from a chaotic CSV file where numbers are written as strings like " 500ms " or "1,000". If you cast directly, it crashes.
raw_data = [" 500 ", "1,000", "N/A", "200"]
cleaned_values = []
for entry in raw_data:
try:
# Step 1: Clean the string (Remove spaces, remove commas)
clean_str = entry.strip().replace(",", "")
# Step 2: Explicit Cast to Integer
val = int(clean_str)
cleaned_values.append(val)
except ValueError:
# Handle cases like "N/A" gracefully
print(f"Skipping invalid data: '{entry}'")
print(f"Cleaned Data: {cleaned_values}")
# Output: [500, 1000, 200]
- Architect Insight: Explicit casting (
int()) is strict. It does not guess. You must “sanitize” the string (remove commas, spaces) before asking Python to convert it.
Lab 7: The “Tuple Backdoor” (Nested Mutability)
Scenario: You defined an “Immutable” configuration tuple to protect your server list. But a hacker (or a buggy function) modified the data anyway.
- Concept: Immutability is shallow. If a tuple holds a list, the list can still change.
# A tuple representing (Datacenter_Name, [Server_List])
dc_config = ("US-East-1", ["Server A", "Server B"])
print(f"Original Config: {dc_config}")
# 1. Try to change the Datacenter Name (Immutable String)
try:
dc_config[0] = "US-West-2"
except TypeError:
print("Blocked: Cannot change Datacenter Name.")
# 2. Try to add a server to the list (Mutable List inside Tuple)
dc_config[1].append("Malicious-Server-X")
print(f"Hacked Config: {dc_config}")
# Output: The list inside is now modified!
- Architect Fix: If you want total immutability, the inner list must also be a tuple:
("US-East-1", ("Server A", "Server B")).
Lab 8: The “Boolean Truth” Trap
Scenario: A deployment script reads ENABLE_DELETION = "False" from a text file. The script accidentally deletes the database because it thought “False” was True.
config_value = "False"
# BAD PRACTICE (Implicit Casting)
# Any non-empty string is evaluated as True in a boolean context
if config_value:
print("DANGER: Deletion Enabled! (The string is not empty)")
# ALSO BAD (Explicit Casting)
# bool("False") is True because the string has characters in it
if bool(config_value):
print("DANGER: bool('False') is actually True!")
# CORRECT PRACTICE (Comparison)
if config_value.lower() == "true":
print("Deletion Enabled.")
else:
print("SAFE: Deletion Disabled.")