Advanced Conditional Logic and Debugging
From the csc241\ curriculum
TL;DR
You'll learn to refine your conditional statements using advanced techniques like ternary operators and match statements for cleaner, more readable code. We'll also cover effective debugging strategies, focusing on using print statements, breakpoints, and stepping through your code to find and fix issues efficiently. Mastering these skills will make your programs more robust and easier to maintain.
1. The Mental Model
Think of advanced conditional logic as a toolkit for making your code's decisions smarter and more compact. Debugging is like being a detective, systematically searching for clues to understand why your program isn't doing what you expect. Both skills are crucial for writing reliable software.
2. The Core Material
2.1 Advanced Conditional Statements

Photo by Pixabay on Pexels
While if/elif/else statements are fundamental, Python offers more concise ways to handle certain conditions.
Ternary Operator
The ternary operator (also called a conditional expression) allows you to assign a value to a variable based on a condition, all on a single line. It's great for simple if/else assignments.
The syntax is: value_if_true if condition else value_if_false
# Traditional if/else
temperature = 25
if temperature > 20:
weather_status = "Warm"
else:
weather_status = "Cool"
print(f"Traditional: {weather_status}") # Output: Traditional: Warm
# Using a ternary operator
weather_status_ternary = "Warm" if temperature > 20 else "Cool"
print(f"Ternary: {weather_status_ternary}") # Output: Ternary: Warm
match Statement (Structural Pattern Matching)
Introduced in Python 3.10, the match statement (also known as structural pattern matching) provides a more powerful and readable way to handle multiple conditions, especially when dealing with different types of data or complex structures. Think of it as a super-powered switch statement from other languages.
# Example: Using match for different command types
def handle_command(command):
match command:
case "start":
return "Starting the service."
case "stop":
return "Stopping the service."
case "restart" | "reload": # You can match multiple values
return "Restarting/reloading the service."
case ["log", level]: # Matching a list with a variable
return f"Logging at level: {level.upper()}"
case {"action": cmd, "user": usr}: # Matching a dictionary
return f"User {usr} requested action: {cmd}"
case _: # The "wildcard" case, catches anything not matched above
return f"Unknown command: {command}"
print(handle_command("start")) # Output: Starting the service.
print(handle_command("reload")) # Output: Restarting/reloading the service.
print(handle_command(["log", "info"])) # Output: Logging at level: INFO
print(handle_command({"action": "create", "user": "alice"})) # Output: User alice requested action: create
print(handle_command("status")) # Output: Unknown command: status
2.2 Debugging Techniques

Photo by Daniil Komov on Pexels
Debugging is the process of finding and fixing errors (bugs) in your code. It's an essential skill.
Print Statement Debugging
The simplest and often first approach is to use print() statements to output the values of variables, confirm execution flow, or mark specific points in your code.
def calculate_average(numbers):
print(f"DEBUG: Input numbers: {numbers}") # Check input
if not numbers:
print("DEBUG: Numbers list is empty, returning 0.")
return 0
total = sum(numbers)
print(f"DEBUG: Sum calculated: {total}") # Check intermediate result
count = len(numbers)
average = total / count
print(f"DEBUG: Average calculated: {average}") # Check final result
return average
result = calculate_average([10, 20, 30])
# Expected output from print statements and actual result
Using a Debugger (Breakpoints and Stepping)
A debugger is a specialized tool that lets you pause your program's execution, inspect variable values, and control how your code runs, line by line. Most Integrated Development Environments (IDEs) like VS Code or PyCharm have excellent built-in debuggers.
Here's the general process:
1. Set a Breakpoint: Click in the gutter next to a line of code where you want the program to pause.
2. Run in Debug Mode: Start your program using the debugger (often a "bug" icon or a specific menu option).
3. Inspect State: When the program hits a breakpoint, it pauses. You can then view the current values of all variables.
4. Step Through Code:
* Step Over: Execute the current line and move to the next. If the line calls a function, it runs the function completely and then moves to the next line after the function call.
* Step Into: If the current line calls a function, Step Into will jump to the first line of that function, allowing you to debug inside it.
* Step Out: If you're inside a function, Step Out will run the rest of the current function and then pause execution at the line immediately after the function call in the calling code.
* Continue: Resume normal program execution until the next breakpoint or the end of the program.
graph TD
A["Start Program"] --> B{Bug suspected?};
B -- Yes --> C["Add `print()` statements"];
C --> D{Problem found?};
D -- No --> E["Set a Breakpoint"];
E --> F["Run in Debug Mode"];
F --> G["Program Paused at Breakpoint"];
G --> H{"Inspect Variables"};
H --> I{"Step Through Code (Over, Into, Out)"};
I --> J{Problem found?};
J -- Yes --> K["Fix Code"];
K --> L["Remove `print()`/Breakpoints"];
L --> M["Test Again"];
D -- Yes --> K;
J -- No --> I;
M -- All clear --> N["Finish Debugging"];
This diagram illustrates a common debugging workflow. You start with print statements, and if that's not enough, you move to using a debugger with breakpoints and stepping.
3. Worked Example
Let's say you're building a simple status checker for network devices. You need to determine a device's health based on its status_code and whether is_reachable is true.
# Initial (buggy) function:
def get_device_health(status_code, is_reachable):
if status_code == 200 and is_reachable == True:
health = "Online and Healthy"
elif status_code == 404:
health = "Not Found"
elif not is_reachable:
health = "Offline"
else:
health = "Unknown Status"
return health
# Test cases
print(f"Device 1: {get_device_health(200, True)}") # Expected: Online and Healthy
print(f"Device 2: {get_device_health(404, True)}") # Expected: Not Found
print(f"Device 3: {get_device_health(200, False)}") # Expected: Offline
print(f"Device 4: {get_device_health(500, True)}") # Expected: Unknown Status
print(f"Device 5: {get_device_health(500, False)}") # Expected: Offline (Incorrect!)
Debugging Device 5:
The output for Device 5 is Unknown Status, but it should be Offline because is_reachable is False.
1. Add print() statements: Let's add them inside the function to see the flow.
```python
def get_device_health(status_code, is_reachable):
print(f"DEBUG: Checking status_code={status_code}, is_reachable={is_reachable}")
if status_code == 200 and is_reachable == True:
health = "Online and Healthy"
print("DEBUG: Condition: Online and Healthy")
elif status_code == 404:
health = "Not Found"
print("DEBUG: Condition: Not Found")
elif not is_reachable: # This is the condition we expect to hit for Device 5
health = "Offline"
print("DEBUG: Condition: Offline")
else:
health = "Unknown Status"
print("DEBUG: Condition: Unknown Status")
return health
print(f"Device 5: {get_device_health(500, False)}")
# Output:
# DEBUG: Checking status_code=500, is_reachable=False
# DEBUG: Condition: Unknown Status
# Device 5: Unknown Status
```
The print statements confirm that for `(500, False)`, the `elif not is_reachable:` block is *not* being hit, and it falls through to the `else`. This indicates an issue with the order of conditions or the conditions themselves.
-
Refactor (Fix): The most specific conditions should often come first, or you need to ensure they don't block more general ones. The
elif not is_reachablecondition should ideally be checked before otherstatus_codechecks if being unreachable is a primary determinant of health.```python
def get_device_health_fixed(status_code, is_reachable):
# Most critical condition first: if it's not reachable, it's offline.
if not is_reachable:
return "Offline"
# Then, check other status codes if it IS reachable.
elif status_code == 200:
return "Online and Healthy"
elif status_code == 404:
return "Not Found"
else:
return "Unknown Status"print("\n--- Fixed Function ---")
print(f"Device 1: {get_device_health_fixed(200, True)}") # Expected: Online and Healthy
print(f"Device 2: {get_device_health_fixed(404, True)}") # Expected: Not Found
print(f"Device 3: {get_device_health_fixed(200, False)}") # Expected: Offline
print(f"Device 4: {get_device_health_fixed(500, True)}") # Expected: Unknown Status
print(f"Device 5: {get
```
Frequently asked about Advanced Conditional Logic and Debugging
More from csc241\
Get the full csc241\ curriculum
Clone the complete plan to your dashboard for unlimited AI-generated notes, practice quizzes, and a personalised revision schedule.
Save this course free