Избегайте, если-иначе, Pythonic Ways

Операторы if-else являются фундаментальной управляющей структурой в программировании. Однако код, который в значительной степени зависит от операторов if-else, менее удобочитаем и менее удобен в сопровождении. К счастью, с Python мы можем легко избежать операторов if-else. В этом сообщении блога мы рассмотрим шесть способов Pythonic, позволяющих избежать операторов if-else.

Следует ли полностью избегать if-elif-else?

Краткий ответ: Нет. Когда условная логика проста, мы можем использовать if-else.

def number_classification(number):
    if number > 0:
        return "Positive"
    elif number < 0:
        return "Negative"
    else:
        return "Zero"

В этом примере мы хотим классифицировать, является ли число положительным, отрицательным или нулем. Логика проста, поэтому можно использовать if-elif-else. Кстати, от блока else здесь можно избавиться.

1. Ненужное еще

def is_even(number):
    if number % 2 == 0:
        return True
    else:
        return False

Приведенный выше код проверяет, является ли число четным. В его логике нет ничего плохого. Однако блок else не нужен, и от него можно легко избавиться.

def is_even(number):
    if number % 2 == 0:
        return True
    return False

Во многих случаях блок else вообще не нужен. Если определенное условие выполнено, просто верните что-то немедленно.

Другой случай, когда от else можно легко избавиться, — это когда if-else используется для присвоения значения переменным. Присвоение значения по умолчанию может значительно упростить ваш код.

# No, we can do better than this
if condition:
    number = 1
else:
    number = 2


# Just use a default vlue
number = 2
if condition:
    number = 1

2. Присвоение ценности

Новые программисты довольно часто используют if-else для присвоения нового значения переменной в зависимости от заданного ввода.

def classify_temperature(temperature):
    message = ''
    if temperature >= 26:
        message = "Hot"
    elif temperature >= 15:
        message = "Warm"
    else:
        message = "Cold"
    return message

В этом примере у нас есть функция, которая принимает температуру в градусах Цельсия и классифицирует температуру как горячую, теплую или холодную. Эта функция очень проста и удобочитаема, но ее еще можно улучшить. Блоки elif и else не нужны.

def classify_temperature(temperature):
    if temperature >= 26:
        return "Hot"
    if temperature >= 15:
        return "Warm"
    return "Cold"

3. Защитные оговорки

Охранные оговорки также известны как досрочное возвращение. Охранные предложения — это способ упростить поток управления в функции или методе, возвращая или вызывая исключение, как только выполняется условие раннего выхода. Это может помочь устранить необходимость в операторах else и сделать код более читабельным, удобным для сопровождения, а также тестируемым. На мой взгляд, нет смысла продолжать работу функции, если она снабжена неверными входными данными.

Код раннего возврата выглядит так:

def my_function():
    if not condition:
        return  # or raise an Exception
    # Do something

def my_function_reverse_condition():
    if condition:
        # Do something
    return # or raise an Exception

В приведенном ниже примере версия с защитными предложениями более удобочитаема, так как каждое условие проверяется и обрабатывается отдельно. Эта версия также не требует нескольких else .

# Using guard clauses
def validate_input(input_list):
    if not isinstance(input_list, list):
        raise TypeError("Input must be a list.")
    if not input_list:
        raise ValueError("Input cannot be an empty list.")
    if not all(isinstance(item, float) for item in input_list):
        raise ValueError("All items in the list must be floats.")
    return True


# Using if-else statements
def validate_input(input_list):
    if isinstance(input_list, list):
        if input_list:
            if all(isinstance(item, float) for item in input_list):
                return True
            else:
                raise ValueError("All items in the list must be floats.")
        else:
            raise ValueError("Input cannot be an empty list.")
    else:
        raise TypeError("Input must be a list.")

4. Словарь

Словарь Python — настоящее обезболивающее, когда нам нужно работать с большим количеством операторов if-else. С помощью словаря Python мы можем сопоставить каждое входное значение с соответствующим действием или значением.

# lots of if-elif
def determine_favorite_fruit(color):
    if color == "red":
        return "Strawberry"
    elif color == "yellow":
        return "Banana"
    elif color == "green":
        return "Honeydew"
    return "unknown"


# Dictionary is used
def determine_favorite_fruit_with_dict(color):
    color_to_fruit = {
        "red": "Strawberry",
        "yellow": "Banana",
        "green": "Honeydew",
    }
    return color_to_fruit.get(color, "unknown")

Использование словарей Python значительно улучшает читаемость нашего кода. Кроме того, он также более гибкий. Бывают случаи, когда нам нужно добавить больше случаев в наш код. При подходе if-elif-else нам нужно добавить в метод еще один блок кода. При подходе со словарем нам просто нужно изменить наш словарь, не затрагивая остальную часть кода. Еще одним преимуществом является повышенная эффективность, поскольку в Python поиск по словарю обычно выполняется быстрее, чем повторение ряда if-else условий.

5. Матч

Начиная с Python 3.10, мы можем использовать match для сопоставления с образцом. Благодаря этой функции мы можем попрощаться с длинными цепочками if-elif .

match value:
    case pattern_1:
        # code to execute if value matches pattern_1
    case pattern_2:
        # code to execute if value matches pattern_2
    ...
    case pattern_n:
        # code to execute if value matches pattern_n
    case _:
        # code to execute if value matches pattern_n

_ (подчеркивание) — это шаблон подстановки, который соответствует любому значению.

def use_if_else(value):
    if value == 1:
        print("Value is 1")
    elif value == 2:
        print("Value is 2")
    elif value == 3:
        print("Value is 3")
    else:
        print("Value is something else")
        
        
def use_match(value):
    match value:
        case 1:
            print("Value is 1")
        case 2:
            print("Value is 2")
        case 3:
            print("Value is 3")
        case _:
            print("Value is something else")

По сравнению с методом if-elif, match делает код более читабельным.

6. Паттерн стратегии

Шаблон стратегии — это шаблон проектирования в разработке программного обеспечения, который позволяет выбирать поведение класса во время выполнения.

Пример: Наш веб-сайт позволяет клиентам использовать различные способы оплаты, такие как кредитные карты и Paypal. Ниже приведен пример не использования шаблона стратегии. Используется довольно много if-elif-else:

def pay_with_credit_card(amount):
    print(f"Paying {amount} using credit card.")

def pay_with_paypal(amount):
    print(f"Paying {amount} using PayPal.")

def pay(amount, method):
    if method == "credit_card":
        pay_with_credit_card(amount)
    elif method == "paypal":
        pay_with_paypal(amount)
    else:
        raise ValueError("Unsupported payment method")

Если stripe также разрешен в качестве способа оплаты, мы должны изменить метод pay. Тем более if-elif-else . Еще одна причина, по которой эта практика плоха: она нарушает принцип открытого-закрытого (SOLID Principles).

def pay_with_credit_card(amount):
    print(f"Paying {amount} using credit card.")

def pay_with_paypal(amount):
    print(f"Paying {amount} using PayPal.")
    
def pay_with_stripe(amount):
    print(f"Paying {amount} using Stripe.")

def pay(amount, method):
    if method == "credit_card":
        pay_with_credit_card(amount)
    elif method == "paypal":
        pay_with_paypal(amount)
    elif method == "stripe":
        pay_with_stripe(amount)
    else:
        raise ValueError("Unsupported payment method")

Мы можем добиться большей ремонтопригодности, ограничив if-elif-else шаблоном стратегии. Когда метод pay вызывается в контексте PaymentContext, он делегирует платеж текущей платежной стратегии.

from abc import ABC, abstractmethod


# Define the abstract Strategy class
class PaymentStrategy(ABC):
    @abstractmethod
    def pay(self, amount):
        pass

    
# Define the concrete Strategy classes
class CreditCardPayment(PaymentStrategy):
    def pay(self, amount):
        print(f"Paying {amount} using credit card.")


class PayPalPayment(PaymentStrategy):
    def pay(self, amount):
        print(f"Paying {amount} using PayPal.")

        
class StripePayment(PaymentStrategy):
    def pay(self, amount):
        print(f"Paying {amount} using Stripe.")
        
        
# Define the Context class that will use the Strategy pattern
class PaymentContext:
    def __init__(self, payment_strategy: PaymentStrategy):
        self.payment_strategy = payment_strategy

    def set_payment_strategy(self, payment_strategy: PaymentStrategy):
        self.payment_strategy = payment_strategy

    def pay(self, amount):
        self.payment_strategy.pay(amount)


# Example usage
payment_context = PaymentContext(CreditCardPayment())
payment_context.pay(100)  # Output: Paying 100 using credit card.

payment_context.set_payment_strategy(PayPalPayment())
payment_context.pay(50)  # Output: Paying 50 using PayPal.

payment_context.set_payment_strategy(StripePayment())
payment_context.pay(150)  # Output: Paying 150 using Stripe.

«🔔 Хотите больше таких статей? Подпишите здесь."

Спасибо за прочтение. Надеюсь, эти 6 трюков будут вам полезны.

Повышение уровня кодирования

Спасибо, что являетесь частью нашего сообщества! Перед тем, как ты уйдешь:

  • 👏 Хлопайте за историю и подписывайтесь на автора 👉
  • 📰 Смотрите больше контента в публикации Level Up Coding
  • 💰 Бесплатный курс собеседования по программированию ⇒ Просмотреть курс
  • 🔔 Подписывайтесь на нас: Twitter | ЛинкедИн | "Новостная рассылка"

🚀👉 Присоединяйтесь к коллективу талантов Level Up и найдите прекрасную работу