← Назад к вопросам

Почему не стоит писать try except с pass?

2.0 Middle🔥 181 комментариев
#Другое

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Почему не стоит писать try-except с pass

Паттерн "try-except с pass" — одна из самых опасных ошибок в Python. Это скрывает ошибки и затрудняет отладку.

Проблема 1: Скрытие ошибок

# ПЛОХО: ошибка незаметна, но её не было
try:
    result = risky_operation()
except Exception:
    pass

print(result)  # NameError: name 'result' is not defined

Это не даёт никакой информации о том, что произошло. Ошибка может быть:

  • ConnectionError при подключении к БД
  • ValueError при парсинге данных
  • TypeError при неправильных аргументах
  • KeyError при доступе к несуществующему ключу

Вы не узнаете, где проблема, пока код не упадёт гораздо позже.

Проблема 2: Невозможно отладить

# Реальный пример: API запрос
def fetch_user_data(user_id: int):
    try:
        response = requests.get(f"https://api.example.com/users/{user_id}", timeout=5)
        return response.json()
    except Exception:
        pass  # Что произошло? Connection timeout? Invalid JSON? Not found?

user_data = fetch_user_data(123)  # None
print(user_data["name"])  # TypeError: 'NoneType' object is not subscriptable

Ошибка выплывет через несколько слоёв кода, и вы не знаете, где она началась.

Проблема 3: Маскирует баги в логике

# ОЧЕНЬ ПЛОХО: баг скрывается
def process_user(user_id):
    try:
        user = User.objects.get(id=user_id)
        user.last_login = datetime.now()  # Баг: забыли timezone
        user.save()
        return user
    except Exception:
        pass  # Баг в datetime скрывается!

user = process_user(1)
if user:  # Может быть None, может быть объект
    print(user.name)

Проблема 4: Усложняет отладку в продакшене

В продакшене вы часто видите:

# ПЛОХО: лог не поможет понять, что произошло
try:
    complex_calculation()
except Exception:
    pass  # Продукция упала, но нет информации

В логах ничего, на мониторинге появляется, что функция вернула None, и дальше всё ломается. Месячный поиск ошибки.

Правильные подходы

1. Логирование исключения

import logging

logger = logging.getLogger(__name__)

def fetch_user_data(user_id: int):
    try:
        response = requests.get(f"https://api.example.com/users/{user_id}")
        response.raise_for_status()  # Выбросить исключение при статусе 4xx/5xx
        return response.json()
    except requests.RequestException as e:
        # Конкретное исключение + логирование
        logger.error(f"Failed to fetch user {user_id}: {e}")
        raise  # Пробросить дальше обработчику

2. Обработка конкретных исключений

def parse_config(config_file: str) -> dict:
    try:
        with open(config_file, "r") as f:
            return json.load(f)
    except FileNotFoundError:
        logger.error(f"Config file not found: {config_file}")
        return get_default_config()  # Fallback
    except json.JSONDecodeError as e:
        logger.error(f"Invalid JSON in config: {e}")
        return get_default_config()  # Fallback
    except IOError as e:
        logger.error(f"Can't read config: {e}")
        raise  # Критическая ошибка, пробросить

3. Возврат значения по умолчанию

def get_optional_data(key: str, default=None):
    try:
        return cache.get(key)
    except CacheError:
        logger.warning(f"Cache error for key {key}, using default")
        return default

4. Context Manager для управления ресурсами

from contextlib import contextmanager

@contextmanager
def database_connection(dsn: str):
    conn = None
    try:
        conn = psycopg2.connect(dsn)
        yield conn
    except psycopg2.Error as e:
        logger.error(f"Database error: {e}")
        if conn:
            conn.rollback()
        raise
    finally:
        if conn:
            conn.close()

# Использование
with database_connection(DSN) as conn:
    conn.execute("SELECT * FROM users")

5. Retry логика вместо молчания

from functools import wraps
import time

def retry(max_attempts=3, delay=1):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_attempts - 1:
                        logger.error(f"Failed after {max_attempts} attempts: {e}")
                        raise
                    logger.warning(f"Attempt {attempt + 1} failed, retrying in {delay}s")
                    time.sleep(delay)
        return wrapper
    return decorator

@retry(max_attempts=3, delay=1)
def unstable_operation():
    return requests.get("https://unstable-api.example.com")

6. Использование finally для очистки

def process_file(filename: str):
    file_handle = None
    try:
        file_handle = open(filename, "r")
        return parse_content(file_handle.read())
    except FileNotFoundError:
        logger.error(f"File not found: {filename}")
        return None
    except ValueError as e:
        logger.error(f"Invalid file format: {e}")
        return None
    finally:
        # Гарантирует закрытие файла в любом случае
        if file_handle:
            file_handle.close()

# Или используй with для автоматического управления
with open(filename, "r") as f:
    return parse_content(f.read())

Правило трёх вопросов

Перед написанием try-except спросите себя:

  1. Какие конкретные исключения я жду?

    • Не пишите except Exception
    • Пишите except (ValueError, KeyError):
  2. Что я хочу сделать, если это произойдёт?

    • Залогировать? ✅
    • Повторить операцию? ✅
    • Вернуть значение по умолчанию? ✅
    • Проигнорировать? ❌ (редко правильный выбор)
  3. Должна ли ошибка пройти выше?

    • Логирую и пробрасываю (raise)
    • Или обрабатываю здесь
# ПРАВИЛЬНО: конкретное исключение + действие
try:
    value = int(user_input)
except ValueError:
    logger.error(f"Invalid integer input: {user_input}")
    value = DEFAULT_VALUE

Пример: трансформация плохого кода

# ПЛОХО: try-except с pass
def load_user(user_id):
    try:
        user = User.objects.get(id=user_id)
        return user
    except:
        pass

# ХОРОШО: конкретное исключение + логирование
def load_user(user_id: int) -> Optional[User]:
    try:
        return User.objects.get(id=user_id)
    except User.DoesNotExist:
        logger.debug(f"User {user_id} not found")
        return None
    except Exception as e:
        logger.error(f"Unexpected error loading user {user_id}: {e}")
        raise  # Критическая ошибка

Инструменты для поиска плохого кода

# flake8 найдёт try-except с pass
flake8 --select=E722 myfile.py

# pylint предупредит
pylint myfile.py
# bare-except: Catching all exceptions (broad-except)

# ruff тоже проверяет
ruff check --select E722 myfile.py

Резюме

Никогда не пишите:

try:
    something()
except:
    pass

Вместо этого:

  • Логируйте исключение с деталями
  • Обрабатывайте конкретные исключения
  • Пробрасывайте дальше, если это критическая ошибка
  • Используйте retry логику для нестабильных операций
  • Примите, что исключения — это информация, а не проблема

Хорошее правило: если вы пишете try-except, вы должны уметь объяснить, почему вы ловите именно это исключение и что делаете с информацией о нём.