Можно ли так использовать try except "вместо if" для управления логикой программы?

Если я в каждой функции при несоблюдении каких-то условий буду выбрасывать исключение это норм? Или так не делается? Можно ли в целом использовать try except для управления логикой приложения и если да то как это правильно и лучше делать?

Просто если в функции не срабатывает исключение, но что-то идет не так как нужно мне. То как это потом отловить. Мне нужно будет каждый раз например проверять что возвращает эта функция, но это вроде уже не так удобно)

Заранее извиняюсь если вопрос глупый, прошу не минусовать) Правда не могу понять. Код ниже для примера.

 # Обработка ошибок в логике
class CustomError(Exception):
    def __init__(self, message):
        super().__init__(message)

    def __str__(self):
        return f"{super().__str__()}"

# Любая функция
def get_yandex(self):
    url = f"https://ya.ru"
    response = requests.get(url)
    if response.status_code == 200:
        return response.json()
    else:
        raise CustomError(f"Ошибка при получении токена: {response.text}") 

Ответы (4 шт):

Автор решения: Intelligent Shade of Blue

Исключения - это предмет постоянных споров. Если они делают вашу программу более читаемой, вы можете их использовать.

Например в питоне, встроенная исключение StopIteration используется для контроля потока итератора. Т.е. в питоне это считается норм.

Вещи которые необходимо держать в уме:

  1. Исключения могут негативно повлиять на производительность. (Особенно в C++).

  2. Если расстояние между где исключение кидается и ловится большое, это может быть неожиданным и ухудшит читаемость вашей программы.

  3. Если вы кидаете ислючения слишком часто, это может затруднить отлаживание вашей программы.

→ Ссылка
Автор решения: Qwertiy

Если для программы нормально происходящее, то так лучше не делать, а если ненормально, то делать.

→ Ссылка
Автор решения: CrazyElf

Ну вот смотрите, если у вас будет, например, такой пример. Вы хотите последовательно сделать несколько действий, объединённых некоей логикой. И при этом не хотите делать лишних действий. То как это будет выглядеть при двух разных подходах. Код довольно условный:

# вариант с if
def aggregate_data(params):
    result1 = get_data1(params)
    if not result1.ok:
        return state.error
    result2 = get_data2(params)
    if not result2.ok:
        return state.error
    result3 = get_aggregate(result1.data, result2.data)
    if not result3.ok:
        return state.error
    return result3

result = aggregate_data(params)
if result != state.error:
    print_aggregate(result)
else:
    print('ошибка!')

# вариант с exception в нижележащих функциях
def aggregate_data(params):
    result1 = get_data1(params)
    result2 = get_data2(params)
    result3 = get_aggregate(result1.data, result2.data)
    return result3

try:
    result = aggregate_data(params)
    print_aggregate(result)
except:
    print('ошибка!')

На мой взгляд использование исключений позволяет избежать лишних проверок. Функция не смогла нормально выполниться - она выбросила ошибку - эта ошибка поймалась там, где вам её удобнее всего обработать. Те блоки кода, которые были зависимы от правильного выполнения предыдущего кода, автоматически не стали выполняться, без дополнительных проверок.

→ Ссылка
Автор решения: MoloF

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

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


class FirstScopedException(Exception):
    pass

class SecondScopedException(Exception):
    pass

class ThirdScopedException(Exception):
    pass


def scenario() -> bool:
    if ...:
        raise FirstScopedException("First Exception")
        
    elif ...:
        raise SecondScopedException("Second Exception")
    
    elif ...:
        raise ThirdScopedException("Third Exception")
    
    
    return True
    

def main():
    try:
        scenario()
    except (FirstScopedException, SecondScopedException, ThirdScopedException) as e:
        print(f"Failed to process the function: {str(e)}")
    finally:
        # Clean Up
        pass

main()

Прочитав исходный код вашей функции вы сразу узнаете какие исключения она может выдать и вы сможете их корректно обработать.

Вы так же можете создать базовый класс чтобы наследовать исключения от него и обрабатывать абсолютно все исключения для вызванной функции в вашем проекте.

Использования try/except как и контекст менеджер гарантирует высвобождение ресурсов которые вы сможете сделать в finally блоке (закрытие соединений, чтение файлов, etc).

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

Дробите большую программу на маленькие части и делайте функции чище по возможности (но не до фанатизма).

→ Ссылка