Условный оператор. Циклы. Исключения [1.8 ]
Продвинутые аспекты условных операторов
В предыдущем разделе мы рассмотрели базовые конструкции условных операторов. Теперь углубимся в более сложные сценарии и паттерны использования.
Множественные условия и логические операторы
При работе со сложными условиями важно понимать приоритет логических операторов и использовать скобки для явного указания порядка вычислений:
# Без скобок - приоритет and выше чем or
age = 25
has_license = True
is_sober = False
# Это условие будет истинным, потому что выполняется (has_license and is_sober) ИЛИ age
# Но логически это неверно!
if age >= 18 or has_license and is_sober:
print("Можно водить") # Неверный вывод!
# Правильный вариант с явными скобками
if age >= 18 and (has_license and is_sober):
print("Можно водить") # Не выведется, что правильно
Цепочки сравнений
Python поддерживает математические цепочки сравнений, что делает код более читаемым:
x = 15
# Вместо:
if x >= 10 and x <= 20:
print("x в диапазоне 10-20")
# Можно написать:
if 10 <= x <= 20:
print("x в диапазоне 10-20")
# Сложные цепочки
y = 25
if 0 < x < y < 100:
print("x и y в допустимом диапазоне")
Сопоставление с образцом (Pattern Matching)
Начиная с Python 3.10 появилась мощная конструкция match-case, которая заменяет множественные ветвления if-elif:
def process_command(command):
match command.split():
case ["quit"]:
return "Выход из программы"
case ["load", filename]:
return f"Загрузка файла: {filename}"
case ["save", filename]:
return f"Сохранение файла: {filename}"
case ["help"] | ["?"]:
return "Справка по командам"
case _:
return f"Неизвестная команда: {command}"
print(process_command("load data.txt")) # Загрузка файла: data.txt
print(process_command("help")) # Справка по командам
print(process_command("delete temp")) # Неизвестная команда: delete temp
Продвинутые техники работы с циклами
Циклы с индексами
Иногда нужно одновременно получить элемент и его индекс. Для этого используется функция enumerate():
fruits = ["яблоко", "банан", "вишня", "груша"]
# Без enumerate
for i in range(len(fruits)):
print(f"{i + 1}. {fruits[i]}")
# С enumerate - более читаемо
for index, fruit in enumerate(fruits, start=1):
print(f"{index}. {fruit}")
# 1. яблоко
# 2. банан
# 3. вишня
# 4. груша
Циклы по нескольким последовательностям
Функция zip() позволяет итерироваться по нескольким последовательностям одновременно:
names = ["Анна", "Борис", "Виктор"]
ages = [25, 32, 19]
cities = ["Москва", "Санкт-Петербург", "Новосибирск"]
for name, age, city in zip(names, ages, cities):
print(f"{name}, {age} лет, живет в {city}")
# Анна, 25 лет, живет в Москва
# Борис, 32 лет, живет в Санкт-Петербург
# Виктор, 19 лет, живет в Новосибирск
Генераторы и выражения-генераторы
Генераторы позволяют создавать эффективные циклы для больших объемов данных:
# Обычное списковое включение
squares = [x**2 for x in range(1000)] # Создает весь список в памяти
# Генераторное выражение
squares_gen = (x**2 for x in range(1000)) # Создает генератор
# Использование генератора
for square in squares_gen:
if square > 100:
break
print(square)
# Преимущество: генератор вычисляет значения по мере необходимости
# и не хранит весь список в памяти
Вложенные циклы и оптимизация
При работе с вложенными циклами важно учитывать производительность:
# Неэффективный вариант - O(n²)
def find_duplicates_slow(numbers):
duplicates = []
for i in range(len(numbers)):
for j in range(i + 1, len(numbers)):
if numbers[i] == numbers[j] and numbers[i] not in duplicates:
duplicates.append(numbers[i])
return duplicates
# Эффективный вариант с использованием множества - O(n)
def find_duplicates_fast(numbers):
seen = set()
duplicates = set()
for num in numbers:
if num in seen:
duplicates.add(num)
else:
seen.add(num)
return list(duplicates)
# Тест производительности
import time
large_list = list(range(10000)) + [42, 99, 150]
start = time.time()
result1 = find_duplicates_slow(large_list)
print(f"Медленный метод: {time.time() - start:.4f} сек")
start = time.time()
result2 = find_duplicates_fast(large_list)
print(f"Быстрый метод: {time.time() - start:.4f} сек")
# Быстрый метод значительно эффективнее для больших данных
Обработка исключений
Исключения — это механизм обработки ошибок и неожиданных ситуаций в программе. В Python исключения генерируются при возникновении ошибок и могут быть перехвачены для обработки.
Базовая структура try-except
try:
# Код, который может вызвать исключение
number = int(input("Введите число: "))
result = 100 / number
print(f"Результат: {result}")
except ValueError:
# Обработка ошибки преобразования в число
print("Ошибка: Введено не числовое значение!")
except ZeroDivisionError:
# Обработка деления на ноль
print("Ошибка: Деление на ноль невозможно!")
except Exception as e:
# Обработка всех остальных исключений
print(f"Произошла неожиданная ошибка: {e}")
else:
# Выполняется, если исключений не было
print("Операция выполнена успешно")
finally:
# Выполняется всегда, независимо от исключений
print("Программа завершена")
Типы исключений и их иерархия
В Python существует иерархия исключений. Важно обрабатывать более конкретные исключения перед общими:
# Иерархия некоторых исключений:
# Exception
# ├── ArithmeticError
# │ ├── ZeroDivisionError
# │ └── OverflowError
# ├── LookupError
# │ ├── IndexError
# │ └── KeyError
# ├── ValueError
# ├── TypeError
# └── IOError
# └── FileNotFoundError
# Пример правильного порядка обработки
def process_data(data):
try:
# Попытка получить элемент по индексу
item = data[0]
# Попытка разделить на этот элемент
result = 10 / item
return result
except IndexError:
print("Ошибка: Список пуст")
except ZeroDivisionError:
print("Ошибка: Деление на ноль")
except TypeError:
print("Ошибка: Неподдерживаемый тип данных")
except Exception as e:
print(f"Неожиданная ошибка: {e}")
Создание пользовательских исключений
Можно создавать свои типы исключений для специфических ситуаций:
# Создание пользовательского исключения
class InvalidAgeError(Exception):
def __init__(self, age, message="Некорректный возраст"):
self.age = age
self.message = message
super().__init__(self.message)
def __str__(self):
return f"{self.message}: {self.age}"
# Использование пользовательского исключения
def check_age(age):
if not isinstance(age, int):
raise TypeError("Возраст должен быть целым числом")
if age < 0:
raise InvalidAgeError(age, "Возраст не может быть отрицательным")
if age > 150:
raise InvalidAgeError(age, "Возраст слишком большой")
return True
# Тестирование
try:
check_age(25) # OK
check_age(-5) # Вызовет InvalidAgeError
except InvalidAgeError as e:
print(e) # Возраст не может быть отрицательным: -5
except TypeError as e:
print(e)
Контекстные менеджеры и исключения
Контекстные менеджеры (оператор with) автоматически обрабатывают исключения при работе с ресурсами:
# Без контекстного менеджера - ручное управление исключениями
file = None
try:
file = open("data.txt", "r", encoding="utf-8")
content = file.read()
print(content)
except FileNotFoundError:
print("Файл не найден!")
except UnicodeDecodeError:
print("Ошибка декодирования файла!")
finally:
if file:
file.close() # Обязательное закрытие файла
# С контекстным менеджером - автоматическое управление
try:
with open("data.txt", "r", encoding="utf-8") as file:
content = file.read()
print(content)
except FileNotFoundError:
print("Файл не найден!")
except UnicodeDecodeError:
print("Ошибка декодирования файла!")
# Файл закроется автоматически даже при исключении
Практические примеры
Пример 1: Валидация пользовательского ввода с повторными попытками
def get_valid_number(prompt, min_value=None, max_value=None, retries=3):
"""
Получает валидное число от пользователя с ограничением попыток
Args:
prompt (str): Сообщение для пользователя
min_value (int/float, optional): Минимальное значение
max_value (int/float, optional): Максимальное значение
retries (int): Количество попыток
Returns:
int/float: Валидное число или None при превышении попыток
"""
for attempt in range(retries):
try:
user_input = input(prompt)
number = float(user_input)
# Проверка диапазона
if min_value is not None and number < min_value:
raise ValueError(f"Число должно быть не меньше {min_value}")
if max_value is not None and number > max_value:
raise ValueError(f"Число должно быть не больше {max_value}")
# Если введено целое число, преобразуем в int
return int(number) if number.is_integer() else number
except ValueError as e:
print(f"Ошибка: {e}")
print(f"Осталось попыток: {retries - attempt - 1}")
except Exception as e:
print(f"Неожиданная ошибка: {e}")
print(f"Осталось попыток: {retries - attempt - 1}")
print("Превышено количество попыток!")
return None
# Использование
age = get_valid_number("Введите ваш возраст (18-100): ", 18, 100)
if age is not None:
print(f"Ваш возраст: {age}")
Пример 2: Обработка сетевых запросов с повторными попытками
import requests
import time
def fetch_data_with_retry(url, max_retries=3, delay=1):
"""
Получает данные по URL с повторными попытками при ошибке
Args:
url (str): URL для запроса
max_retries (int): Максимальное количество попыток
delay (int): Задержка между попытками в секундах
Returns:
dict: Данные в формате JSON или None при неудаче
"""
for attempt in range(max_retries):
try:
print(f"Попытка {attempt + 1}/{max_retries}...")
response = requests.get(url, timeout=5)
response.raise_for_status() # Вызовет исключение при HTTP ошибке
return response.json()
except requests.exceptions.ConnectionError:
print("Ошибка соединения. Проверьте интернет-соединение.")
except requests.exceptions.Timeout:
print("Таймаут запроса. Сервер не отвечает.")
except requests.exceptions.HTTPError as e:
print(f"HTTP ошибка: {e.response.status_code}")
except requests.exceptions.RequestException as e:
print(f"Ошибка запроса: {e}")
except ValueError:
print("Ошибка декодирования JSON")
if attempt < max_retries - 1:
print(f"Повторная попытка через {delay} секунд...")
time.sleep(delay)
delay *= 2 # Экспоненциальная задержка
print("Все попытки исчерпаны!")
return None
# Использование (требует установки requests)
# data = fetch_data_with_retry("https://api.github.com/users/octocat")
# if data:
# print(f"Имя пользователя: {data['name']}")
Пример 3: Безопасная работа с базой данных
import sqlite3
from contextlib import contextmanager
# Создание контекстного менеджера для базы данных
@contextmanager
def database_connection(db_name):
conn = None
try:
conn = sqlite3.connect(db_name)
conn.row_factory = sqlite3.Row # Для доступа по именам колонок
yield conn
except sqlite3.Error as e:
print(f"Ошибка базы данных: {e}")
if conn:
conn.rollback()
raise
finally:
if conn:
conn.close()
# Функция с обработкой исключений
def get_user_by_id(user_id):
try:
with database_connection("users.db") as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
user = cursor.fetchone()
if user is None:
raise ValueError(f"Пользователь с ID {user_id} не найден")
return dict(user) # Преобразуем в словарь для удобства
except ValueError as e:
print(f"Ошибка валидации: {e}")
return None
except sqlite3.Error as e:
print(f"Ошибка базы данных: {e}")
return None
except Exception as e:
print(f"Неожиданная ошибка: {e}")
return None
# Использование
user = get_user_by_id(123)
if user:
print(f"Имя пользователя: {user['name']}")
else:
print("Пользователь не найден или произошла ошибка")
ЧЕК-ЛИСТ ПО СТАТЬЕ 1.8: УСЛОВНЫЙ ОПЕРАТОР, ЦИКЛЫ, ИСКЛЮЧЕНИЯ
| № | Навык / Знание | Понимаю и умею применять | Нужно повторить |
|---|---|---|---|
| 1 | Условные операторы
|
||
| 2 | Функции для работы с циклами |
||
| 3 | Базовые исключения
|
||
| 4 | Пользовательские исключения
|
||
| 5 | Контекстные менеджеры
|
||
| 6 | Валидация ввода
|
||
| 7 | Обработка сетевых ошибок
|
||
| 8 | Работа с БД и файлами
|
||
| 9 | Лучшие практики
|
||
| 10 | Отладка и тестирование
|
Заключение
В этой статье мы углубились в продвинутые аспекты условных операторов и циклов, а также изучили важную тему обработки исключений. Эти навыки позволяют создавать более надежные, эффективные и читаемые программы.
Понимание того, как правильно обрабатывать ошибки и исключительные ситуации, отличает начинающего разработчика от профессионала. Обработка исключений не только предотвращает аварийное завершение программы, но и делает взаимодействие с пользователем более удобным и информативным.
В следующей статье мы рассмотрим функциональное программирование в Python и более продвинутые техники работы с данными.
Приложение 1: Исключения в Python
Ключевое слово raise используется в Python для явной генерации (выброса) исключений. Это позволяет создавать исключения при нарушении условий, повышать уровень перехваченных исключений или вызывать пользовательские исключения для специфичных ситуаций.
Таблицы исключений Python
Таблица 1: Основные встроенные исключения
| Категория | Исключение | Когда возникает | Пример |
|---|---|---|---|
| Базовые | Exception |
Базовый класс всех исключений | except Exception as e: |
BaseException |
Родитель всех исключений | Редко используется напрямую | |
| Арифметические | ArithmeticError |
Базовый класс для арифметических ошибок | В иерархии исключений |
ZeroDivisionError |
Деление на ноль | 10 / 0 |
|
OverflowError |
Результат операции слишком велик | 10 ** 1000 (в некоторых случаях) |
|
| Типы данных | TypeError |
Неправильный тип данных | "5" + 3 |
ValueError |
Неправильное значение | int("abc") |
|
| Работа с коллекциями | LookupError |
Базовый класс для ошибок поиска | В иерархии исключений |
IndexError |
Индекс вне диапазона | lst[10] для списка из 5 элементов |
|
KeyError |
Ключ не найден в словаре | dct["несуществующий_ключ"] |
|
| Ввод-вывод | IOError/OSError |
Ошибка ввода-вывода | Проблемы с чтением/записью файлов |
FileNotFoundError |
Файл не найден | open("несуществующий_файл.txt") |
|
| Атрибуты | AttributeError |
Атрибут объекта не найден | obj.несуществующий_атрибут |
| Импорт | ImportError |
Ошибка импорта модуля | import несуществующий_модуль |
Таблица 2: Специализированные исключения
| Библиотека | Исключение | Когда возникает | Пример обработки |
|---|---|---|---|
| Requests (сетевые запросы) |
ConnectionError |
Проблемы с сетевым соединением | except requests.exceptions.ConnectionError: |
Timeout |
Таймаут запроса | except requests.exceptions.Timeout: |
|
HTTPError |
HTTP ошибка (4xx, 5xx) | except requests.exceptions.HTTPError: |
|
RequestException |
Базовый класс всех исключений requests | except requests.exceptions.RequestException: |
|
| SQLite3 (работа с БД) |
sqlite3.Error |
Базовый класс ошибок SQLite | except sqlite3.Error as e: |
sqlite3.IntegrityError |
Нарушение целостности данных | except sqlite3.IntegrityError: |
|
sqlite3.OperationalError |
Ошибка операций с БД | except sqlite3.OperationalError: |
|
| JSON | json.JSONDecodeError |
Ошибка декодирования JSON | except json.JSONDecodeError: |
Практические примеры с raise
def calculate_average(numbers): if not numbers: raise ValueError("Список чисел не должен быть пустым") if not all(isinstance(x, (int, float)) for x in numbers): raise TypeError("Все элементы должны быть числами") return sum(numbers) / len(numbers)
def process_file(filename): try: with open(filename, 'r') as file: return file.read() except PermissionError: print(f"Нет доступа к файлу {filename}") raise # Повторно выбрасываем то же исключение
def parse_user_input(input_str): try: return int(input_str) except ValueError as e: raise ValueError(f"Невозможно преобразовать '{input_str}' в число") from e
Иерархия исключений (сокращенная)
BaseException
├── KeyboardInterrupt
├── SystemExit
└── Exception
├── ArithmeticError
│ ├── ZeroDivisionError
│ └── OverflowError
├── LookupError
│ ├── IndexError
│ └── KeyError
├── ValueError
├── TypeError
├── IOError
│ └── FileNotFoundError
└── [Пользовательские исключения]
Лучшие практики работы с исключениями
except:finally или контекстные менеджеры (with)Шпаргалка по обработке исключений
try: # Код, который может вызвать исключение risky_operation() except SpecificException as e: # Обработка конкретного исключения print(f"Конкретная ошибка: {e}") except (AnotherException, DifferentException) as e: # Обработка нескольких типов исключений print(f"Одна из ошибок: {e}") except Exception as e: # Обработка всех остальных исключений print(f"Неожиданная ошибка: {e}") # Логирование или повторный выброс raise else: # Выполняется, если исключений не было print("Операция успешна") finally: # Выполняется всегда print("Завершение операции")
Примечание: Это приложение является дополнением к основной статье об условных операторах, циклах и исключениях. Здесь собрана справочная информация для быстрого доступа и повторения материала.
Ссылки по теме
- Функции в Python: основные строительные блоки программ [1.5]
- Ввод и вывод данных. Форматирование. Работа с файлами. [1.6 ]
- Условный оператор и циклы в Python [1.7]
![Условный оператор. Циклы. Исключения [1.8 ] Условный оператор. Циклы. Исключения [1.8 ]](https://www.technobee.ru/media/zoo/images/line100_python_24f065d5f44b128d7987e916e0a668ad.png)