Lab 1: The Swapper
# Traditional way (Don't do this in Python)
a = 5
b = 10
temp = a
a = b
b = temp
print(a, b)
# Pythonic way (Do this!)
x = 50
y = 100
x, y = y, x
print(f"x is now {x}, y is now {y}")Lab 2: Advanced Unpacking
server_config = ("192.168.1.1", 8080, "Production", "AWS", "Linux")
# Extract IP and Port, keep the rest as metadata
ip, port, *metadata = server_config
print(f"Connecting to {ip} on {port}")
print(f"Server Info: {metadata}")Lab 3: The “Mutable Assignment” Trap (Critical)
Scenario: You want to initialize three different server lists for Development, QA, and Production. You decide to use “Chained Assignment” to save time.
- The Trap: When you assign a Mutable object (like a List) using
a = b = [], you aren’t creating three lists. You are creating one list with three tags. If you add a server to Dev, it magically appears in Prod too!
print("--- The Trap ---")
# BAD PRACTICE for Mutable Objects
dev_servers = qa_servers = prod_servers = []
# We add a server ONLY to 'dev_servers'
dev_servers.append("dev-db-01")
print(f"Dev: {dev_servers}")
print(f"Prod: {prod_servers}")
# Output: Prod: ['dev-db-01']
# DISASTER! Production list got modified because they are the SAME object.
print("\n--- The Fix ---")
# CORRECT PRACTICE
# Create independent lists
dev_servers = []
qa_servers = []
prod_servers = []
dev_servers.append("dev-api-01")
print(f"Dev: {dev_servers}")
print(f"Prod: {prod_servers}")
# Output: Prod: [] (Clean and Safe)
- Architect Insight: Never use chained assignment (
x = y = []) for lists or dictionaries. Only use it for immutable things like numbers, booleans, or strings (x = y = 0).
Lab 4: Log Parsing with Star Operator (*)
Scenario: You are parsing raw access logs from an Apache/Nginx server. The log format is [Timestamp, IP, Method, Path, ...UserAgent...].
- Challenge: The User Agent string at the end might be split into many parts by spaces, or it might be short. You don’t know the length.
- Solution: Use the Star Operator to grab “everything else” into a list.
raw_log_line = ["2024-01-20", "192.168.1.5", "GET", "/login", "Mozilla/5.0", "(Windows", "NT", "10.0)"]
# We want specific fields for the first 4 items.
# We want to bundle the rest (User Agent) into one variable.
timestamp, client_ip, method, path, *user_agent_parts = raw_log_line
print(f"Alert: {client_ip} tried to access {path}")
# Reconstruct the User Agent string from the list
full_user_agent = " ".join(user_agent_parts)
print(f"User Agent: {full_user_agent}")
Output:
Alert: 192.168.1.5 tried to access /login
User Agent: Mozilla/5.0 (Windows NT 10.0)
Lab 5: The “Ignore” Convention (Using _)
Scenario: You are calling a function (like an AWS SDK method) that returns a tuple with 3 values: (Status, Message, RequestID).
- Goal: You only care about the
Status. You don’t want to clutter your code with variables namedmsgorreq_idthat you never use. - Solution: Use the underscore
_as a “trash can” variable.
# Simulating a function that returns multiple values
def reboot_instance():
return ("SUCCESS", "Reboot initiated", "req-abc-123")
# Standard Unpacking:
# status, msg, req_id = reboot_instance() -> Wastes memory if we don't use msg/req_id
# Professional Unpacking:
# Use '_' for values you want to ignore.
status, _, _ = reboot_instance()
print(f"Operation Status: {status}")
- Architect Insight: This is highly recommended by PEP 8 (Python’s style guide). It tells other developers: “I know these values exist, but I am intentionally ignoring them.”
Lab 6: Deep Dive into Reference Counting
Scenario: Let’s prove that variables are just “Tags” and see exactly when Python decides to delete an object.
import sys
# 1. Create an object "CyberSecurity"
tag_1 = "CyberSecurity"
# 2. Check references
# Note: getrefcount returns a higher number because the function call itself creates a temporary reference.
print(f"Initial Refs: {sys.getrefcount(tag_1)}")
# 3. Add a second tag
tag_2 = tag_1
print(f"Refs after tag_2: {sys.getrefcount(tag_1)}") # Count increases
# 4. Remove tags
del tag_1
print("Deleted tag_1...")
# The object "CyberSecurity" is NOT deleted yet because tag_2 still holds it.
print(f"tag_2 still holds: {tag_2}")
# 5. Remove final tag
del tag_2
# Now the reference count hits 0. The Garbage Collector will eat the object immediatelOutput
Initial Refs: 4
Refs after tag_2: 5
Deleted tag_1...
tag_2 still holds: CyberSecurity