Insights Videos Blog Learning
PYTHON

Part 2: Smarter Scripts, Realer Logic

Make your Python scripts respond, decide, and feel alive—with real-world input, variables, and logic that adapts.

In Part 1, you learned how to talk to your computer using print(), collect user inputs, apply some branching logic using if / else, and math operators like +, -, *, and /. Now, lets take it to the next level. In this post, we'll look into loops, lists and much more.

You can use the code editor below to test everything we learn. Just type, run, and see what happens! Prefer your own setup? You can also code on Replit or any other Python editor. Totally your call!

Tips for using the editor below:
  • Click the hamburger menu (top-left) to open the sidebar. And you can do the following:
    • Reset to start fresh anytime.
    • Download to save your code. You can also download the code as a zip file.
    • Fullscreen mode = more space! Press Esc to exit.

Recap + Setup for Smarter Logic

Part 1 gave us the fundamentals, but now let's refine those tools for more powerful building. Remember math operations or how we did type checking? We'll take them a step further and look into modulo and type conversion to ensure our understanding is rock-solid before we start looking into loops, lists and more.

# Scenario: You're routing incoming calls to agents using a round-robin strategy.

# Define a list of agents. Lists hold multiple values — perfect for routing scenarios. (More on lists later)
agents = ["Alice", "Bob", "Charlie", "Dana", "Eli"]
total_agents = len(agents)  # Count how many agents are available

# Let's say this is the 127th call of the day.
call_number_str = "127"  # Still a string! We need to convert it.
call_number = int(call_number_str)  # Convert to an integer so we can do math

# Determine which agent to assign using modulo.
# Why (call_number - 1)? Because Python indexing starts at 0,
# but human count usually starts at 1. Adjusting keeps things accurate.
assigned_agent_index = (call_number - 1) % total_agents
assigned_agent = agents[assigned_agent_index]  # Look up the agent from the list

# Output the result
print(f"Call #{call_number} is assigned to agent: {assigned_agent}")
Call #127 is assigned to agent: Bob
Note:

I want to call out a few things from the example above.

  • len() gives you the number of items in a collection — it works on lists, strings, and more.
    For example: print(len("Alice")) will return 5.
  • We used # on each line to write multi-line comments. Technically, """ works too — but it can act like a docstring in some cases, which may cause issues in advanced code. Stick to # for clarity and safety.
  • You might notice that we used (call_number - 1). That's because Python starts indexing at 0. For example, in a list like ["a", "b", "c", "d"], Python sees "a" as position 0, "b" as 1, and so on. But humans count from 1. Subtracting 1 aligns the math with how we naturally count.
  • In the last post, we used type() to check data types. Now you're seeing why that matters! The value "127" is a string, not a number — so doing "127" - 1 would cause a TypeError. That's why we convert it first using int() to get a proper number for calculations.

Nested if/else and Cleaner Inputs

In the last post, we learned basic if/else for simple "this or that" decisions. But real-world scenarios are rarely so linear. You might have to make a decision, and then, based on that decision, make another decision. This is where nested conditionals come into play – one if statement inside another.

Think about a live chat bot that first identifies the customer's status, and then, based on that status, asks further questions or routes them differently:

# Get and clean the customer's input for their account status
account_status = input("Enter your account status (active / suspended / new): ").strip().lower()

if account_status == "active":
    # If active, then check their query type
    query = input("What can I help you with (billing / technical / general)? ").strip().lower()
    if query == "billing":
        print("Routing to Billing Support.")
    elif query == "technical":
        print("Connecting to Technical Support.")
    else:
        print("Directing to FAQ for general queries.")
elif account_status == "suspended":
    # If suspended, a different set of actions/routing
    print("Please contact us during business hours for suspended accounts.")
    # You might already be able to think of business hours logic here.
else: # This covers "new_customer" or any other input
    print("Welcome! Routing you to New Customer Onboarding.")

Your turn:

Expand the "suspended" account logic. If the `account_status` is "suspended", then ask them for their "suspended reason" (e.g., "payment_issue", "policy_violation"). If the `reason` is "payment_issue", print "Directing to Payment Recovery." Otherwise, print "Please wait for a specialist."

Comparison and Logical Operators

Beyond simple if/else choices, real-world CX logic often needs to check multiple conditions at once. Python's comparison operators let you check relationships (like == or >), while logical operators (and, or, not) let you combine or invert those checks for powerful decision-making.

Think about a customer service bot determining priority:

  • == (equals)
  • != (not equal)
  • > (greater than), < (less than)
  • >= (greater than or equal), <= (less than or equal)
  • and (both conditions must be true)
  • or (at least one condition must be true)
  • not (inverts a condition)
# Scenario: Prioritizing a customer for specialized support
customer_tier = "gold"
issue_type = "technical"
current_wait_time = 8 # minutes

# Logic: Route to specialized team if 'gold' customer AND a 'technical' issue,
# OR if it's a 'platinum' customer (regardless of issue type).
if (customer_tier == "gold" and issue_type == "technical") or customer_tier == "platinum":
    print("Routing to Specialized Technical Support.")
elif customer_tier == "silver" and current_wait_time > 5:
    print("Prioritizing for general support due to long wait.")
else:
    print("Standard support queue.")

# Output based on above: Routing to Specialized Technical Support.

You might have come across such examples in your daily work, think of skills based routing, skill expansion logic, etc.

Your turn:

Modify the code above to trigger an "Urgent Callback Alert" if:

  1. The customer_tier is "platinum" and their current_wait_time is more than 3 minutes, OR
  2. The customer_tier is "vip" and the issue_type is "billing".

Lists: Handle Multiple Values

We already got introduced to Lists when we had that example about a list of agents we need to route the calls to using round robin approach. Lists help you work with multiple inputs — think agent names, customer names, intents, or tickets. They’re like containers that hold ordered data.

tickets = ["reset", "refund", "escalation"]
print(tickets[0])  # first item at index 0
Note:
  • You'll receive an IndexError when you try to access a position that doesn't exist in the list.
  • Lists are zero-indexed. (we already knew that from examples above) tickets[1] is the second item, not the first!

for Loops: Process the List

Lists are great for storing multiple items, but how do you do something with each one? That’s where for loops shine. They let you repeat an action for every item in a list — or in any group of items, like a string, tuple, or set. Think of it like processing a queue of customer interactions, support tickets, or daily tasks.

Basic Iteration: Handling Each Item

The most common use is to simply visit every item in your list:

# Scenario: Iterating through a list of pending support tickets
pending_tickets = ["Password Reset", "Billing Inquiry", "Feature Request", "Account Suspension"]

print("--- Processing Daily Support Queue ---")
for ticket in pending_tickets:
    print(f"Now handling ticket: {ticket}")
    # Imagine here your code would trigger an action: send email, update status, etc.
print("--- Queue Processed ---")

# Output:
# --- Processing Daily Support Queue ---
# Now handling ticket: Password Reset
# Now handling ticket: Billing Inquiry
# Now handling ticket: Feature Request
# Now handling ticket: Account Suspension
# --- Queue Processed ---

Counting with Loops: The Power of range()

Sometimes you need to repeat an action a specific number of times, or perhaps you need to work with items in a list using their **numerical position (index)**. That's where the range() function shines! It generates a sequence of numbers, perfect for controlling how many times a loop runs or for accessing elements at specific positions.

# Scenario: Simulating a batch process for sending daily customer reports
# Imagine we have 5 reports to generate, perhaps for 5 different customer segments.

num_reports_to_send = 5

print("--- Generating Daily Reports ---")
# range(5) will give us numbers 0, 1, 2, 3, 4
for report_number in range(num_reports_to_send):
    # We add 1 to 'report_number' to make it user-friendly (Report 1, 2, etc.)
    print(f"Sending Report for Segment {report_number + 1}...")
    # In a real system, this might trigger a function to create and send the report
print("--- All Reports Scheduled! ---")

# Output:
# --- Generating Daily Reports ---
# Sending Report for Segment 1...
# Sending Report for Segment 2...
# Sending Report for Segment 3...
# Sending Report for Segment 4...
# Sending Report for Segment 5...
# --- All Reports Scheduled! ---

Combining Both: enumerate() for Item and Index

You've seen how to loop through items directly 'for item in list:' and how to count with range() for repetition. But what if you need both? Often, when processing data, you need to know not just what the item is, but also its position or order within the list. Python's enumerate() function is a lifesaver here: it gives you both the index (its position, starting from 0) and the item itself, neatly packaged for you in each loop.

This is incredibly useful for scenarios like:

  • Displaying numbered lists (e.g., "Question 1: ...", "Item 2: ...")
  • Tracking progress based on an item's position
  • Assigning a sequential ID to items as you process them
# Scenario: Displaying customer survey responses with their corresponding question numbers
survey_responses = ["Satisfied", "Neutral", "Very Satisfied", "Dissatisfied"]

print("\n--- Customer Survey Results Breakdown ---")
# enumerate() gives us 'index' (0, 1, 2, 3...) and 'response' for each item
for index, response in enumerate(survey_responses):
    # We add 1 to 'index' so the questions are numbered starting from 1 (more human-friendly)
    print(f"Response to Question #{index + 1}: {response}")

# Output:
# --- Customer Survey Results Breakdown ---
# Response to Question #1: Satisfied
# Response to Question #2: Neutral
# Response to Question #3: Very Satisfied
# Response to Question #4: Dissatisfied
Note:

Loops are how we automate repetitive work — like sending alerts, checking logs, or building reports. These things often happen behind the scenes in your tools, but when you're the one writing the logic, and you need to "do something for each item," a for loop is your best friend.

Your turn:

You have a list of new agent names: new_agents = ["Alice", "Bob", "Charlie", "David"].

  1. Use a for loop to print a welcome message for each agent (e.g., "Welcome, Alice!").
  2. Now, **using enumerate()**, modify your loop to also print which "Training Group" they belong to, if you assign them sequentially (Group 1, Group 2, etc., starting from Group 1 for the first agent).

List Slicing & Access Tricks

Python gives you powerful shortcuts to access specific parts of a list. These are called list slices, and they're incredibly handy when you only need a portion of your data — think of grabbing the latest customer feedback, checking the last few calls in a queue, or even quickly reversing an interaction history.

Grabbing the First Few Items ([:end])

When you need the items from the beginning of a list up to a certain point (but not including that point), you use [:end]. This is perfect for showing recent activity or a limited set of options.

# Scenario: Displaying the top 3 urgent tickets in a queue
urgent_tickets = ["Password Reset Failed", "Service Down Report", "High Volume Billing Query", "Account Lockout", "Feature Request"]

# Get the first 3 tickets (indices 0, 1, 2)
top_3_urgent = urgent_tickets[:3]
print(f"Top 3 Urgent Tickets: {top_3_urgent}")

# Output:
# Top 3 Urgent Tickets: ['Password Reset Failed', 'Service Down Report', 'High Volume Billing Query']

Accessing Items from a Specific Start ([start:])

If you want to get everything from a certain point in your list right up to the end, use [start:]. This is useful for processing remaining items or logs.

# Scenario: Checking customer feedback from a specific point onward
all_feedback_scores = [5, 4, 5, 3, 5, 4, 2, 5]
# Let's say the first 3 scores were from an older batch, we want the rest
recent_feedback = all_feedback_scores[3:]
print(f"Recent Feedback Scores: {recent_feedback}")

# Output:
# Recent Feedback Scores: [3, 5, 4, 2, 5]

Getting Specific Ranges ([start:end])

You can also define both a start and an end point to grab a specific "middle" section of your list.

# Scenario: Extracting a specific segment of a customer's call history
call_history = ["Agent John - 09:00", "Bot - 09:15", "Agent Jane - 09:30", "Agent Mark - 09:45", "Bot - 10:00"]

# Get calls from the 2nd to the 4th interaction (index 1 up to, but not including, index 4)
segment_of_calls = call_history[1:4]
print(f"Interactions from 09:15 to 09:45: {segment_of_calls}")

# Output:
# Interactions from 09:15 to 09:45: ['Bot - 09:15', 'Agent Jane - 09:30', 'Agent Mark - 09:45']

Quick Access: Last Item ([-1]) and Reversing ([::-1])

Negative indices let you count from the end of the list. [-1] always gives you the very last item. And if you need to quickly reverse the order of items (like seeing the most recent call first), [::-1] is a fantastic trick.

# Scenario: Checking the most recent customer interaction and reviewing full history in reverse
customer_interactions = ["Email received (10:00)", "Call initiated (10:30)", "Chat started (10:45)", "SMS sent (11:00)"]

# Get the very last interaction (most recent)
last_interaction = customer_interactions[-1]
print(f"Most Recent Interaction: {last_interaction}")

# View the entire interaction history from most recent to oldest
reversed_history = customer_interactions[::-1]
print(f"Full History (Newest First): {reversed_history}")

# Output:
# Most Recent Interaction: SMS sent (11:00)
# Full History (Newest First): ['SMS sent (11:00)', 'Chat started (10:45)', 'Call initiated (10:30)', 'Email received (10:00)']
Note:

Remember, Python lists are zero-indexed! This means the first item is at index 0, the second at 1, and so on. Also, when slicing, the end index is always exclusive (the item at that index is not included).

Your turn:

You have a list of recent callers in a queue: recent_callers = ["Emily", "Frank", "Grace", "Henry", "Ivy"].

  1. Print the caller who is currently at the very end of the queue.
  2. Print only the last three callers in the list using a slice.
  3. Print all callers except the first and the last one.

List Methods You’ll Use Often

Lists in Python aren’t just containers — they come with powerful built-in methods that make it easy to work with data. Whether you’re managing queues, updating logs, or checking customer records, these methods are handy when you're writing the logic yourself.

Adding Items: .append()

Use .append() to add a new item to the very end of your list. Think of a new customer joining a waiting queue or a new event being logged.

# Scenario: Adding a new caller to the support queue
support_queue = ["Alice (VIP)", "Bob (Loyal)", "Charlie (Guest)"]
print(f"Queue before new caller: {support_queue}")

# A new caller joins the queue
new_caller = "David (New Customer)"
support_queue.append(new_caller)
print(f"Queue after new caller: {support_queue}")

# Output:
# Queue before new caller: ['Alice (VIP)', 'Bob (Loyal)', 'Charlie (Guest)']
# Queue after new caller: ['Alice (VIP)', 'Bob (Loyal)', 'Charlie (Guest)', 'David (New Customer)']

Checking Item Count: len()

We’ve seen len() used with strings — it works the same way with lists. len() (short for “length”) tells you how many items are in the list. It’s useful for checking the number of items in the list, for example checking queue sizes, counting tasks, or even detecting if a list is empty.

# Scenario: Monitoring the size of an active chat queue
active_chats = ["Chat-101", "Chat-102", "Chat-103"]
print(f"Current active chats: {active_chats}")

queue_size = len(active_chats)
print(f"Number of active chats: {queue_size}")

# Output:
# Current active chats: ['Chat-101', 'Chat-102', 'Chat-103']
# Number of active chats: 3

Checking for Existence: in Operator

The in operator is incredibly useful for quickly checking if a specific item (for example, a customer ID, a ticket status, or a keyword) exists within your list. It returns either True or False.

# Scenario: Verifying if a customer is on the VIP list
vip_customers = ["Prasanna", "Anjali", "Jay"]
customer_name = "Anjali"
search_name = "David"

is_vip_anjali = customer_name in vip_customers
is_vip_david = search_name in vip_customers

print(f"Is {customer_name} a VIP? {is_vip_anjali}")
print(f"Is {search_name} a VIP? {is_vip_david}")

# Output:
# Is Anjali a VIP? True
# Is David a VIP? False

Removing Items: .remove()

When an item is processed or no longer needed (like a ticket being closed or a caller leaving the queue), you can remove it by its value using .remove(). Note: it removes only the first occurrence if there are duplicates.

# Scenario: Removing a completed task from a daily to-do list
daily_tasks = ["Respond to email", "Update CRM", "Process Refund", "Follow up on ticket"]
print(f"Tasks before completion: {daily_tasks}")

completed_task = "Update CRM"
daily_tasks.remove(completed_task)
print(f"Tasks after completing '{completed_task}': {daily_tasks}")

# Output:
# Tasks before completion: ['Respond to email', 'Update CRM', 'Process Refund', 'Follow up on ticket']
# Tasks after completing 'Update CRM': ['Respond to email', 'Process Refund', 'Follow up on ticket']

Sorting Items: .sort()

The .sort() method sorts the items in your list in-place (meaning it modifies the original list). By default, it sorts alphabetically for strings and numerically for numbers. This is handy for organizing data for reports or display.


# Scenario: Organizing a list of agent names alphabetically
agent_names = ["Zara", "Alice", "Mark", "David"]
print(f"Agents unsorted: {agent_names}")

agent_names.sort() # Sorts the list alphabetically
print(f"Agents sorted: {agent_names}")

# Output:
# Agents unsorted: ['Zara', 'Alice', 'Mark', 'David']
# Agents sorted: ['Alice', 'David', 'Mark', 'Zara']
Tip:

These list methods are your toolkit for managing dynamic data in automation scripts. They let you easily simulate and control lists of items — just like in real-world scenarios where you're calling a function mid-flow to make decisions, collect data, or queue up tasks before processing an interaction.

Your turn:

You're tracking customer feedback tags: feedback_tags = ["Bug Report", "Feature Request", "UI Issue", "Bug Report", "Login Problem"].

  1. Add a new tag: "Performance Issue".
  2. Count how many tags are currently in the list.
  3. Check if the tag "Login Problem" exists in the list (print True or False).
  4. Remove the first occurrence of "Bug Report".
  5. Sort the final list of tags alphabetically.
  6. Print the list after each step to see the changes!

Other Concepts You’ll Start Noticing

These ideas will start popping up naturally as you code more. You don’t need to memorize them all now — just get familiar with how they behave.

Concept Description & Example
+= Augmented assignment: count += 1
Example: count = 2; count += 1 → 3
\n, \t Escape sequences for newlines and tabs
Example: print("Line1\nLine2")
Output:
Line1
Line2
Expressions Returns a value: 2 + 3 or "Hi " + name
Example: 4 * 5 → 20
Statements Performs an action: print(), if, for
Example: print("Hi!") → Outputs: Hi!
List unpacking Split list into variables:
a, b = ["X", "Y"]
Now a = "X", b = "Y"
Operator precedence Order matters! (Pssst... Remember this from primary school math class?)
(2 + 3) * 4 → 20
2 + 3 * 4 → 14 (multiplies first)
Your turn:

Try combining these in your own mini scripts. Don't rush to memorize — learning by doing is the best way to retain them.

Mini Project: Daily Ticket Processing & Prioritization

Let's bring together all the powerful tools you've learned in Part 2. We'll build a script that simulates the start of an automated daily workflow for processing incoming support tickets. This system will:

  • Start with a list of new incoming tickets.
  • Loop through each ticket to evaluate it.
  • Use nested if/else and logical operators to decide the priority/routing for each ticket.
  • Dynamically display status messages using f-strings.
  • Generate a simple summary using len() and potentially update the list with .remove().

# Scenario: An automated script processes a batch of daily incoming support tickets.

# 1. Our list of incoming tickets
incoming_tickets = [
    "Billing Inquiry - High Priority",
    "Password Reset - Low Priority",
    "Service Outage - Critical",
    "Feature Request - Medium Priority",
    "Refund Request - High Priority"
]

print("--- Starting Daily Ticket Scan ---")
tickets_processed_count = 0
critical_alerts_sent = 0

# 2. Loop through each ticket in our list
for ticket in incoming_tickets:
    tickets_processed_count += 1 # Augmented assignment (+=) from our bonus concepts!

    ticket_text = ticket.lower() # Convert to lowercase for easier comparisons

    print(f"\nProcessing ticket #{tickets_processed_count}: '{ticket}'")

    # 3. Use nested logic and operators to prioritize and route
    if "critical" in ticket_text: # Using 'in' operator to check for keywords
        print("ACTION: IMMEDIATE ESCALATION TO SRE TEAM!")
        critical_alerts_sent += 1
    elif "high priority" in ticket_text:
        print("ACTION: Assign to Senior Agent Queue.")
        # We could imagine removing it from this list or moving it to another list here
    elif "billing inquiry" in ticket_text and "refund" not in ticket_text:
        print("ACTION: Route to Billing Department, standard queue.")
    elif "password reset" in ticket_text:
        print("ACTION: Direct to Automated Password Flow.")
    else:
        print("ACTION: Assign to General Support Queue.")

print("\n--- Daily Scan Complete ---")
print(f"Total tickets processed: {tickets_processed_count}")
print(f"Critical alerts triggered: {critical_alerts_sent}")
print(f"Remaining tickets in original queue (for demo): {incoming_tickets}") # Original list remains for demo purposes
Tip:

This little script demonstrates the core of many automated systems. You define your data (the list of tickets), then you use loops and logic to process each piece, taking different actions based on its content. This is how you start building systems that can handle hundreds or thousands of interactions automatically!

Your Challenge:

Make this routing system even smarter!

  1. Add a new ticket type to the incoming_tickets list, perhaps "New Feature Request - Urgent".
  2. Modify the for loop to use enumerate(). Print the ticket's number (e.g., "Ticket #1: ...") along with its processing message.
  3. Add a new `elif` condition to handle "New Feature Request" tickets. Maybe they get routed to a `Product Team Queue`?
  4. After processing all tickets, use **list slicing** to display only the "top 2 most critical tickets" if they were found (imagine you had a separate `critical_tickets_list.append()` inside the loop).

Gotchas and How to Avoid Them

  • Indentation matters: Python uses indentation to group blocks. If something isn’t indented properly, it will throw an error like IndentationError: expected an indented block.
  • Always convert input: Values from input() are strings. Use int() or float() before doing math.
  • Use type() to debug: Not sure what kind of data you’re working with? Run type(your_variable) to find out.
  • Lists start at index 0: Accessing mylist[1] gives you the second item. Watch out for IndexError.
  • Break things on purpose: Testing wrong inputs helps you understand the boundaries and build muscle memory.

Up Next: Data Structures & Smarter Logic

In Part 3, we’ll go beyond simple lists and logic to build smarter, more flexible programs. You’ll learn how to:

  • Use dictionaries to store and retrieve key-value data
  • Understand tuples and why some data should be immutable
  • Work with sets to handle uniqueness
  • Build mock agent dashboards, session logs, and smarter decision trees

These data structures are the foundation for everything from config files to CX state machines. See you there!

A Note Before You Go (or move to the next post)

You’ve probably noticed most examples here are rooted in CX — routing calls, analyzing feedback, managing tickets, etc. That’s not by accident. This blog is CX-themed, because that’s my day job. I work on contact centers, CPaaS, and AI-powered automation — so these are the use cases that come naturally.

That said, most of these functions — like routing, queueing, or prioritizing — are already productized in the real world. You’ve likely seen them inside modern bot builders, customer service platforms, or low-code workflows. They’re often visual and GUI-driven, but underneath, there’s always some logic like what we’re writing here (though not necessarily in Python).

So if you're from the CX world, great — this stuff should feel relatable, maybe even spark ideas on how things work under the hood. But if you're from a different field, adapt freely. Insurance? Use Python to simulate premium calculations. Logistics? Model freight routing or delivery schedules. The point is: make it yours.

It sure beats doing GPA calculators, age checkers, or BMI scripts — unless you're building those for real users.

Wherever you're from, the advice stays the same: test your basics after every post. Tweak the code. Write your own versions. Break it, fix it. That’s how you actually learn.

Need a sandbox to sharpen your skills? Try:

Real progress happens when you stop skimming and start digging. One hour of focused tinkering beats a week of passive scrolling.