Let’s go through some Python switch statement alternatives! Because Python (as of version 3.12) does not have a built-in switch statement, a feature common in languages like C, C++, Java, and JavaScript, we have to find alternative ways to achieve similar functionality in our code. That is what this article is about. Let’s get started 🔥🔥🔥.
CONTENTS
Why Python Doesn’t Have A Switch Statement
Alternative 1: Classic If-Else Chains
Alternative 2: Dictionaries Approach
Alternative 3: Classes and Methods Approach
Alternative 4: Match-Case Approach
Why Python Doesn’t Have A Switch Statement
Switch case statements are used in programming languages for controlling the flow of a program by allowing the execution of different parts of code depending on the value of a variable. Essentially, they offer a more streamlined and readable alternative to a long series of if-else statements.
In the C++ programming language a basic construct of a classic switch…case statement will look something like:
switch (expression) {
case constant1:
// code to be executed if expression equals constant1
break;
case constant2:
// code to be executed if expression equals constant2
break;
// You can have any number of case statements
default:
// code to be executed if expression doesn't match any case
}
Let’s explain what is happening here:
- switch (expression): The switch keyword is followed by a variable or an expression in parentheses. The value of this expression is used to determine which case to execute.
- case constantX: Each case is labeled with the case keyword followed by a constant value. If the expression matches this constant value, the code under this case is executed.
- break: The break keyword is crucial. It tells the program to exit the switch block. Without break, the program will continue executing into the next case regardless of the match (this is known as fall-through).
- default: The default case is optional. It executes if none of the other cases match the expression. Think of it as the “else” of a switch case.
Surprisingly, Python doesn’t have this specific construct. So the natural question is “why on earth doesn’t this programming language have a switch statement?”. The explanation is that Python emphasizes readability and simplicity in its design philosophy. The language’s creator, Guido van Rossum, and the Python community have often preferred using existing Python constructs like dictionaries and functions over adding a switch statement. The idea is to keep the language simple and readable.
This explanation is plausible and understandable. Readable code is easily maintainable code and that is one of the main goals of developers. Maintainability actually may rank higher than performance, right next to security, as projects scale in size.
Not all of the alternatives to the switch statement are created equal though. As we shall see, Python actually recommends a specific choice which we will reveal later in our article. Nonetheless, each alternative will function identically to the classic C++ case…switch construct at its core, even though the code will look different on the surface.
Alternative 1: Classic If-Else Chains
This is the first alternative we will explore.
In a simple scenario, an if-else chain might be used to make basic decisions based on a single condition. For example, determining if a number is positive, negative, or zero:
number = 5
if number > 0:
print("The number is positive.")
elif number < 0:
print("The number is negative.")
else:
print("The number is zero.")
In this example, the program checks if number is greater than, less than, or equal to zero and prints a message accordingly. This style is probably by far the most common approach:
A complex if-else chain can involve multiple conditions, nested if-else structures, and more intricate logic. Here’s an example where we’re determining a student’s grade based on their score, considering various conditions:
score = 85
grade = ''
attendance_rate = 0.95
extra_curricular = True
if score >= 90:
grade = 'A'
if attendance_rate < 0.8:
grade = 'B'
elif extra_curricular:
grade = 'A+'
elif 80 <= score < 90:
grade = 'B'
if attendance_rate < 0.8:
grade = 'C'
elif extra_curricular:
grade = 'B+'
elif 70 <= score < 80:
grade = 'C'
if attendance_rate < 0.8:
grade = 'D'
elif extra_curricular:
grade = 'C+'
else:
grade = 'F'
print("The student's grade is:", grade)
In this more complex example, the student’s grade is determined not only by their score but also by their attendance rate and participation in extra-curricular activities. The if-else chain is nested, meaning there are if-else structures within an if-else structure. This allows for more detailed and nuanced decision-making based on multiple criteria.
Here are some things to note about this Classic If-Else chains:
- Each case is identified with an if then an elif keyword for subsequent cases in that branch, along with the matching condition for that case.
- For each case the code to execute is presented right after it, as an indented series of statements.
- You use nested if statements to evaluate specific sub-cases within larger cases.
- The base or default case is presented after the else: keyword.
- Pros are that this approach is simple, readable, flexible and efficient for a small number of cases.
- Cons are it’s limited scalability, higher potential for duplication, difficulty debugging and potential performance issues with long chains.
Let move on to alternative 2 of our list of Python switch statement alternatives.
Alternative 2: Dictionaries Approach
A Python dictionary is a collection type that stores data in a key-value pair format, allowing for efficient retrieval, modification, and storage of data. Each key in a dictionary is unique and acts as an identifier for its corresponding value, and the values can be of any data type.
Using dictionaries as an alternative to switch statements in Python can be an effective way to manage multiple conditions and actions.
In this simple example, we’ll create a dictionary to simulate a basic calculator that can perform addition, subtraction, multiplication, and division based on user input.
def add(x, y):
return x + y
def subtract(x, y):
return x - y
def multiply(x, y):
return x * y
def divide(x, y):
return x / y
calculator = {
'add': add,
'subtract': subtract,
'multiply': multiply,
'divide': divide
}
operation = 'multiply'
result = calculator[operation](7896, 32383)
print(f"Result: {result}")
This code defines four basic arithmetic operations as functions (add, subtract, multiply, divide) and stores them in a dictionary named calculator. The dictionary keys are the operation names (e.g., ‘add’), and the values are the corresponding function objects. When the user specifies an operation (here, ‘multiply’) and provides two numbers, the appropriate function is called, and the result is printed.
In a more complex scenario, we can use a dictionary to manage different user roles and their respective permissions in a system.
def admin_access():
return "Admin: Full access to the system."
def user_access():
return "User: Limited access to the system."
def guest_access():
return "Guest: View-only access."
role_permissions = {
'admin': admin_access,
'user': user_access,
'guest': guest_access
}
user_role = 'user'
access = role_permissions.get(user_role, lambda: "No access")()
print(f"Access level: {access}")
This code manages different user roles and their corresponding access levels within a system. It defines three functions (admin_access, user_access, guest_access) that return strings describing the level of access for each role. A dictionary role_permissions maps role names (e.g., ‘admin’) to these functions. The user’s role (here, ‘user’) is used to fetch the appropriate function from the dictionary. If the role does not exist in the dictionary, a default lambda function returns “No access”. The selected function is then called to determine the access level, and the result is printed.
There’s a few things we can say here:
- Each case is identified in a def statement or function. This is convenient because it allows us to define the logic for each case in a familiar manner.
- Each case is mapped to a key which invokes the function associated with the case.
- Default case is handled easily using dict.get() method and the lambda statement.
- Dictionaries can store functions, making it easy to associate actions with keys.
- This approach can be more readable than a series of if-elif-else statements, especially for a large number of cases.
- It’s easy to add or remove cases by simply modifying the dictionary.
- For very simple scenarios, using a dictionary might be overkill compared to if-elif-else statements. Each case requires a function, which might lead to unnecessary function definitions for simple actions.
- Care must be taken to manage keys and ensure they correctly map to the intended functions or actions.
- Beginners might find this pattern less intuitive than classic if-elif-else chains.
Let move on to alternative 3 of our list of Python switch statement alternatives.
Alternative 3: Classes and Methods Approach
Using classes and methods in Python as an alternative to switch statements can be a powerful way to handle different cases with more flexibility and extensibility. Here’s a brief explanation of this approach along with simple and more complex examples.
Consider a scenario where you need to perform basic arithmetic operations based on user input:
class ArithmeticOperation:
def execute(self, a, b):
pass
class Add(ArithmeticOperation):
def execute(self, a, b):
return a + b
class Subtract(ArithmeticOperation):
def execute(self, a, b):
return a - b
# Usage
operations = {
"add": Add(),
"subtract": Subtract()
}
operation = input("Enter operation (add/subtract): ")
result = operations[operation].execute(5, 3)
print(f"Result: {result}")
In this example, instead of using a switch statement or a series of if-else conditions, we define a base class ArithmeticOperation and specific classes for each operation that inherit from it. The execute method is overridden in each subclass to perform the specific operation.
Consider a more complex situation involving a text-based adventure game where different commands lead to different actions:
class GameAction:
def execute(self, player):
pass
class Attack(GameAction):
def execute(self, player):
player.attack()
class Heal(GameAction):
def execute(self, player):
player.heal()
class Flee(GameAction):
def execute(self, player):
player.flee()
# Usage
actions = {
"attack": Attack(),
"heal": Heal(),
"flee": Flee()
}
command = input("Enter command (attack/heal/flee): ")
player = Player() # Assume Player is a defined class
actions[command].execute(player)
In this complex example, we define a GameAction class and subclasses for each game action. Each subclass implements the execute method, which performs an action on the Player object. This pattern allows for easy extension with new actions.
There’s a few things we can say here:
- Each case is defined as separate class. The statements to be executed for each case are accessed through the class object.
- Dictionaries are used here as well to map the case to the class object pertinent to each case. There also isn’t a default case so all cases have to be handled explicitly.
- Easy to add new cases without modifying existing code.
- Each case’s logic is contained in its own class, leading to cleaner and more maintainable code. Classes and methods can be reused in different parts of the application.
- More classes and files may be needed, which can be overkill for simple scenarios.
- Can increase complexity, making the code harder to understand for beginners.
- Adds a level of indirection which might impact performance slightly, especially in very simple cases where a basic switch or if-else would suffice.
Let move on to alternative 4 of our list of Python switch statement alternatives.
Alternative 4: Match-Case Approach
The Match-Case approach is really the preferred method and the best alternative to the C/C++ switch…case structure.
Introduced in Python 3.10, it is a structured way to match patterns and is Python’s official answer to the traditional switch-case statement found in many other programming languages. It’s defined in PEP 634, PEP 635, and PEP 636, which lay out the syntax, motivation, and rationale behind this feature.
Prior to Python 3.10 this particular functionality is NOT available in Python programming language. If you attempt to use it with older versions of Python you will get a SyntaxError: invalid syntax error.
In a basic scenario, let’s say you want to print messages based on the type of fruit:
fruit = "apple"
match fruit:
case "apple":
print("Apple is red")
case "banana":
print("Banana is yellow")
case _:
print("Unknown fruit")
This example demonstrates how you can match the fruit variable against several patterns (in this case, string literals). The underscore _ acts as a wildcard, catching any cases that don’t match the specified patterns.
We see a match keyword and a case keyword but no switch keyword. Still, this is pretty much the closest you’ll get to the classic switch…case structure.
In a more complex scenario, the match-case statement can be used with data structures, like dictionaries, and can even unpack values:
user = {"name": "Alice", "age": 30, "role": "admin"}
match user:
case {"name": name, "age": age, "role": "admin"}:
print(f"Admin {name} is {age} years old.")
case {"name": name, "age": age}:
print(f"User {name} is {age} years old.")
case _:
print("Unknown user")
Here, the match-case statement is used to pattern-match against a dictionary. It not only checks for the presence of keys but also binds their values to variables (name and age), which can then be used in the code.
Here is yet another example:
class Circle:
def __init__(self, radius):
self.radius = radius
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
class Triangle:
def __init__(self, base, height):
self.base = base
self.height = height
def calculate_area(shape):
match shape:
case Circle(radius=radius):
return 3.14 * radius ** 2
case Rectangle(length=length, width=width):
return length * width
case Triangle(base=base, height=height):
return 0.5 * base * height
case _:
raise ValueError("Unknown shape")
# Example usage
circle = Circle(radius=5)
rectangle = Rectangle(length=4, width=6)
triangle = Triangle(base=3, height=5)
print("Circle Area:", calculate_area(circle))
print("Rectangle Area:", calculate_area(rectangle))
print("Triangle Area:", calculate_area(triangle))
We define classes for different shapes: Circle, Rectangle, and Triangle, each with their relevant dimensions.
The calculate_area function uses the match-case statement to determine the shape it’s dealing with and calculates its area accordingly.
Pattern matching in the match-case statement checks the type of the shape and binds its attributes to variables, allowing for easy computation of the area.
The wildcard case _ provides error handling for unknown shapes.
There is a few things we can say here about the match…case approach :
- The underscore _ acts as a wildcard that denotes the default case.
- Each case is identified in a clear, concise manner making this really the best Python alternative to the switch…case statement.
- More readable and cleaner than nested if-else blocks, especially for complex matching.
- Allows for sophisticated pattern matching, including unpacking of data structures.
- In some cases, it can ensure that all possible cases are handled (though Python’s implementation doesn’t enforce this).
- New syntax and concepts, such as pattern matching, can be initially challenging to grasp.
- Only available in Python 3.10 and later, limiting its use in older codebases.
- For very simple conditional logic, traditional if-else might be more straightforward and intuitive.
So there it is! You now have 4 ways to work around the lack of a C/C++ type switch…case structure. Alternative 4 is really the closest to that traditional approach but there are other ways you can achieve the same results in Python.
Check out our other great Python tutorials HERE.
Thanks for reading! 👌🏻👌🏻👌🏻