Python Dynamic Typing Lab

Lab 1: The Shapeshifter (Dynamic Reassignment) Observe how the ID changes when the Type changes.

var = 100
print(f"Value: {var}, Type: {type(var)}, ID: {id(var)}")

var = "DevOps"
print(f"Value: {var}, Type: {type(var)}, ID: {id(var)}")
# Notice the ID is totally different. It's a new object.

Lab 2: The “Type Guard” (Architect Pattern) How to safely handle dynamic types in a function.

def process_data(input_data):
    if isinstance(input_data, list):
        print(f"Processing list with {len(input_data)} items.")
    elif isinstance(input_data, str):
        print(f"Processing string: {input_data.upper()}")
    else:
        print("Error: Unsupported type!")

process_data(["Server1", "Server2"]) # Handles List
process_data("localhost")            # Handles String
process_data(12345)                  # Handles Error safely

Lab 3: The “Duck Typing” Logger (Polymorphism)

Scenario: You are writing a logging function for your cloud infrastructure. You want this function to write logs to any destination—a physical file, a terminal, or a network stream—without changing the code.

  • Concept: Duck Typing. Python doesn’t care if the object is officially a “File”. It only asks: “Does this object have a .write() method?” If yes, it works.
class FakeNetworkStream:
    def write(self, message):
        print(f"[Network Packet Sent]: {message}")

def secure_logger(output_device, message):
    """
    I don't care what 'output_device' is.
    I only care if I can .write() to it.
    """
    output_device.write(f"LOG: {message}\n")

# 1. Use a Real File (Standard)
with open("system.log", "w") as f:
    secure_logger(f, "System started.")

# 2. Use the Terminal (Standard Output)
import sys
secure_logger(sys.stdout, "System running...")

# 3. Use a Custom Class (Duck Typing)
network = FakeNetworkStream()
secure_logger(network, "System alert!")

Output:

  • system.log file is created.
  • Terminal shows: LOG: System running...
  • Terminal shows: [Network Packet Sent]: LOG: System alert!
  • Architect Insight: This makes Python incredibly flexible for testing. You can swap a real AWS S3 uploader with a “Fake” uploader class during unit tests easily.

Lab 4: The “Mixed List” Crash (Handling Dynamic Data)

Scenario: You scraped a website for prices, but the data is messy. Some are numbers (100), some are strings ("200"), and some are missing (None). You need to calculate the total cost.

  • The Trap: A simple loop will crash with a TypeError.
  • The Fix: Handle types dynamically inside the loop.
raw_prices = [100, "200", None, "50", 400, "Free"]

total_cost = 0

for item in raw_prices:
    # Check the type before acting
    if isinstance(item, int):
        total_cost += item
        
    elif isinstance(item, str):
        # Try to convert string to int
        if item.isdigit():
            total_cost += int(item)
        else:
            print(f"Skipping text: '{item}'")
            
    elif item is None:
        print("Skipping missing data...")

print(f"Total Cost: ${total_cost}")

Output:

Skipping missing data...
Skipping text: 'Free'
Total Cost: $750

Lab 5: The “API Response” Switcher

Scenario: You are querying a Cloud API.

  • If you search for one server, it returns a Dictionary.
  • If you search for all servers, it returns a List.
  • Challenge: Your script must handle both return types dynamically to avoid crashing.
def process_api_response(response):
    # Dynamic Type Checking
    if isinstance(response, dict):
        # Handle Single Object
        print(f"Processing Single Server: {response.get('id')}")
        
    elif isinstance(response, list):
        # Handle List of Objects
        print(f"Processing Batch of {len(response)} Servers:")
        for server in response:
            print(f" - {server.get('id')}")
            
    else:
        print("Error: Unknown API format.")

# Simulating API Calls
api_single = {"id": "i-12345", "state": "running"}
api_batch = [{"id": "i-67890"}, {"id": "i-11223"}]

process_api_response(api_single) # Works
process_api_response(api_batch)  # Works too!
  • Architect Insight: This pattern is everywhere in AWS Boto3 scripts. Always check isinstance() before iterating!

Lab 6: Type Hinting (The “Modern” Python)

Scenario: You are working on a large team. You write a function to calculate server uptime. You want to tell your teammates explicitly that start_time must be an integer (timestamp), not a string.

# The syntax: variable: type
# The arrow -> type: indicates return type

def calculate_uptime(start_time: int, current_time: int) -> int:
    return current_time - start_time

# Correct Usage
uptime = calculate_uptime(1600000000, 1600003600)
print(f"Uptime: {uptime} seconds")

# Incorrect Usage (Python runs it, but IDEs like VS Code will underline it in RED)
# This helps catch bugs before you even run the code!
# crash = calculate_uptime("yesterday", "today") 

Lab 7: The id() Reassignment Proof

Scenario: Visually proving that “Changing a variable” is actually “Moving a tag to a new box.”

# Tag 'x' is on Box 50
x = 50
addr1 = id(x)
print(f"x is {x} at address {addr1}")

# Move Tag 'x' to Box 60
x = 60
addr2 = id(x)
print(f"x is {x} at address {addr2}")

# Check if they are the same spot in memory
if addr1 != addr2:
    print("Proof: The variable moved to a completely new memory location!")

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top