Python

Модуль 2: Управление потоком

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

2.1 Условные операторы

Что такое условные операторы?

Условные операторы позволяют программе выполнять разные блоки кода в зависимости от заданных условий. В Python основным условным оператором является if.

Оператор if-elif-else

Конструкция if-elif-else позволяет проверить несколько условий последовательно:

age = 18

if age < 13:
    print("Ребёнок")
elif age < 18:
    print("Подросток")
else:
    print("Взрослый")
    
# Результат: Взрослый

Важно: В Python для создания блоков кода используются отступы (обычно 4 пробела), а не фигурные скобки как во многих других языках.

Операторы сравнения

Для создания условий используются операторы сравнения:

Таблица операторов сравнения

Оператор Описание Пример
== Равно x == y
!= Не равно x != y
> Больше чем x > y
< Меньше чем x < y
>= Больше или равно x >= y
<= Меньше или равно x <= y

Логические операторы

Для объединения условий используются логические операторы:

# Оператор and (И): оба условия должны быть истинными
age = 25
income = 50000

if age > 21 and income > 30000:
    print("Кредит одобрен")
    
# Оператор or (ИЛИ): хотя бы одно условие должно быть истинным
if age > 65 or income < 10000:
    print("Предоставляется скидка")
    
# Оператор not (НЕ): инвертирует результат
is_weekend = False
if not is_weekend:
    print("Рабочий день")

Тернарный оператор

Python поддерживает краткий способ записи условия в одну строку — тернарный оператор:

# Синтаксис: значение_если_истина if условие else значение_если_ложь
age = 20
status = "совершеннолетний" if age >= 18 else "несовершеннолетний"
print(status)  # Результат: совершеннолетний

Проверка принадлежности и идентичности

Python предлагает специальные операторы для проверки принадлежности элемента к коллекции и идентичности объектов:

# Оператор in проверяет наличие элемента в последовательности
fruits = ["яблоко", "банан", "груша"]
if "банан" in fruits:
    print("Банан в списке")
    
# Операторы is и is not проверяют, указывают ли переменные на один и тот же объект
a = [1, 2, 3]
b = a        # b указывает на тот же список
c = [1, 2, 3]  # c - новый список с такими же значениями

print(a is b)    # True
print(a is c)    # False
print(a == c)    # True (содержимое равно)

Советы для практики

  • Начинайте с простых условий и постепенно усложняйте их.
  • Обращайте внимание на отступы — они определяют блоки кода в Python.
  • Помните о приоритете операторов при создании сложных условий.
  • Используйте скобки для группировки условий для лучшей читаемости.

Практическое задание

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

  • Если возраст меньше 0, выведите "Ошибка: возраст не может быть отрицательным"
  • Если возраст от 0 до 12, выведите "Ребенок"
  • Если возраст от 13 до 17, выведите "Подросток"
  • Если возраст от 18 до 64, выведите "Взрослый"
  • Если возраст 65 и старше, выведите "Пенсионер"
Показать решение
age = int(input("Введите ваш возраст: "))

if age < 0:
    print("Ошибка: возраст не может быть отрицательным")
elif age <= 12:
    print("Ребенок")
elif age <= 17:
    print("Подросток")
elif age <= 64:
    print("Взрослый")
else:
    print("Пенсионер")

2.2 Циклы (for/while)

Циклы позволяют выполнять один и тот же блок кода несколько раз. В Python есть два основных типа циклов: for и while.

Цикл for

Цикл for используется для итерации по последовательностям (спискам, кортежам, строкам и т.д.) или другим итерируемым объектам.

# Базовый синтаксис цикла for
for элемент in последовательность:
    # код, который выполняется для каждого элемента
    
# Пример: перебор элементов списка
fruits = ["яблоко", "банан", "груша"]
for fruit in fruits:
    print(f"Я люблю {fruit}")

# Результат:
# Я люблю яблоко
# Я люблю банан
# Я люблю груша

Функция range()

Функция range() часто используется с циклом for для создания последовательности чисел:

# range(stop) - числа от 0 до stop-1
for i in range(5):
    print(i)
# Результат: 0 1 2 3 4

# range(start, stop) - числа от start до stop-1
for i in range(2, 6):
    print(i)
# Результат: 2 3 4 5

# range(start, stop, step) - числа от start до stop-1 с шагом step
for i in range(1, 10, 2):
    print(i)
# Результат: 1 3 5 7 9

Цикл while

Цикл while выполняет блок кода, пока заданное условие остаётся истинным:

# Базовый синтаксис цикла while
while условие:
    # код, который выполняется пока условие истинно

# Пример: счётчик с while
count = 0
while count < 5:
    print(count)
    count += 1  # увеличиваем счётчик
# Результат: 0 1 2 3 4

Предупреждение: Будьте осторожны с циклами while! Если условие никогда не станет ложным, получится бесконечный цикл. Всегда проверяйте, что условие в какой-то момент изменится.

Управляющие операторы: break и continue

Python предоставляет специальные операторы для контроля выполнения циклов:

# break - немедленно прекращает выполнение цикла
for i in range(10):
    if i == 5:
        break  # выход из цикла при i = 5
    print(i)
# Результат: 0 1 2 3 4

# continue - пропускает текущую итерацию и переходит к следующей
for i in range(10):
    if i % 2 == 0:
        continue  # пропускаем четные числа
    print(i)
# Результат: 1 3 5 7 9

Цикл for-else и while-else

В Python циклы могут иметь блок else, который выполняется после завершения цикла, если цикл не был прерван с помощью break:

# Пример использования else с циклом for
for i in range(5):
    print(i)
else:
    print("Цикл завершен нормально")
# Результат: 0 1 2 3 4 Цикл завершен нормально

# Пример с прерыванием цикла
for i in range(5):
    if i == 3:
        break  # прерываем цикл
    print(i)
else:
    print("Это сообщение не будет выведено")
# Результат: 0 1 2

Вложенные циклы

Циклы можно вкладывать друг в друга для обработки многомерных данных:

# Пример вложенных циклов: вывод таблицы умножения
for i in range(1, 4):  # внешний цикл: строки
    for j in range(1, 4):  # внутренний цикл: столбцы
        print(f"{i} × {j} = {i*j}", end="\t")
    print()  # переход на новую строку после каждой строки таблицы

# Результат:
# 1 × 1 = 1   1 × 2 = 2   1 × 3 = 3   
# 2 × 1 = 2   2 × 2 = 4   2 × 3 = 6   
# 3 × 1 = 3   3 × 2 = 6   3 × 3 = 9  

Генераторы списков

Генераторы списков (list comprehensions) — это краткий и элегантный способ создания списков на основе существующих последовательностей:

# Синтаксис: [выражение for элемент in последовательность if условие]

# Создание списка квадратов чисел
numbers = [1, 2, 3, 4, 5]
squares = [x**2 for x in numbers]
print(squares)  # [1, 4, 9, 16, 25]

# С условием: только четные числа
even_squares = [x**2 for x in numbers if x % 2 == 0]
print(even_squares)  # [4, 16]

# Вложенные циклы в генераторе
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat_list = [x for row in matrix for x in row]
print(flat_list)  # [1, 2, 3, 4, 5, 6, 7, 8, 9]

Когда использовать for, а когда while?

  • Используйте for: когда заранее известно количество итераций или нужно перебрать все элементы последовательности.
  • Используйте while: когда количество итераций заранее неизвестно и выполнение зависит от условия.

Советы для эффективной работы с циклами

  • Избегайте изменения последовательности, по которой итерируется цикл for.
  • Используйте функцию enumerate() для получения и индекса, и значения элемента: for index, value in enumerate(sequence):
  • Для параллельной итерации по нескольким последовательностям применяйте zip(): for x, y in zip(list1, list2):
  • Всегда проверяйте условие выхода из цикла while, чтобы избежать бесконечных циклов.

Практические задания

Задание 1: Напишите программу, которая выводит таблицу умножения для числа, введенного пользователем (от 1 до 10).

Показать решение
number = int(input("Введите число от 1 до 10: "))

print(f"Таблица умножения для числа {number}:")
for i in range(1, 11):
    result = number * i
    print(f"{number} × {i} = {result}")

Задание 2: Напишите программу, которая находит все числа от 1 до 100, которые делятся на 7, используя генератор списков.

Показать решение
divisible_by_7 = [num for num in range(1, 101) if num % 7 == 0]
print("Числа от 1 до 100, делящиеся на 7:")
print(divisible_by_7)

2.3 Исключения

Исключения — это объекты, которые представляют ошибки и особые ситуации, возникающие во время выполнения программы. В Python механизм обработки исключений позволяет корректно реагировать на ошибки, не прерывая работу программы.

Блок try-except

Основной конструкцией для обработки исключений является блок try-except:

# Базовый синтаксис try-except
try:
    # Код, который может вызвать исключение
    result = 10 / 0  # Деление на ноль вызывает ZeroDivisionError
except ZeroDivisionError:
    # Код, который выполняется при возникновении указанного исключения
    print("Ошибка: деление на ноль!")
    
# Результат: Ошибка: деление на ноль!

Блок try-except-else-finally

Полная структура блока обработки исключений включает дополнительные секции else и finally:

try:
    # Код, который может вызвать исключение
    num = int(input("Введите число: "))
    result = 100 / num
except ValueError:
    # Выполняется при ошибке преобразования в int
    print("Введено не число!")
except ZeroDivisionError:
    # Выполняется при делении на ноль
    print("Деление на ноль!")
else:
    # Выполняется, если в блоке try не возникло исключений
    print(f"Результат: {result}")
finally:
    # Выполняется всегда, независимо от того, было ли исключение
    print("Операция завершена")

Важно: Блок finally выполняется всегда, вне зависимости от того, возникло исключение или нет. Это делает его идеальным местом для освобождения ресурсов (закрытие файлов, соединений с базой данных и т.д.).

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

Несколько типов исключений можно обрабатывать разными способами:

# Несколько блоков except для разных исключений
try:
    value = int(input("Введите число: "))
    result = 10 / value
except ValueError:
    print("Некорректный ввод: не число")
except ZeroDivisionError:
    print("Ошибка: деление на ноль")
    
# Несколько исключений в одном блоке except
try:
    value = int(input("Введите число: "))
    result = 10 / value
except (ValueError, ZeroDivisionError):
    print("Произошла ошибка: неверный ввод или деление на ноль")

Перехват всех исключений

Python позволяет перехватывать все исключения, но этот подход следует использовать с осторожностью:

# Перехват любого исключения - избегайте такой практики в реальном коде!
try:
    # Какой-то код
    result = 10 / 0
except Exception as e:
    # Получаем доступ к объекту исключения
    print(f"Произошла ошибка: {e}")
    
# Результат: Произошла ошибка: division by zero

Предупреждение: Перехват всех исключений с помощью except Exception или просто except: может скрыть неожиданные ошибки и затруднить отладку. Лучше перехватывать только конкретные исключения, которые вы ожидаете и умеете обрабатывать.

Вызов исключений

Вы можете генерировать исключения самостоятельно с помощью оператора raise:

# Генерация стандартного исключения
def validate_age(age):
    if age < 0:
        raise ValueError("Возраст не может быть отрицательным")
    if age > 120:
        raise ValueError("Недопустимый возраст")
    return age
    
# Пример использования
try:
    user_age = validate_age(-5)
except ValueError as error:
    print(f"Ошибка: {error}")
    
# Результат: Ошибка: Возраст не может быть отрицательным

Создание собственных исключений

Вы можете создавать собственные классы исключений, наследуя их от базового класса Exception:

# Определение пользовательского исключения
class InsufficientFundsError(Exception):
    """Вызывается при попытке снять больше денег, чем есть на счёте"""
    pass
    
# Использование пользовательского исключения
class BankAccount:
    def __init__(self, balance=0):
        self.balance = balance
        
    def withdraw(self, amount):
        if amount > self.balance:
            raise InsufficientFundsError(f"Недостаточно средств. Баланс: {self.balance}, требуется: {amount}")
        self.balance -= amount
        return amount
        
# Пример использования
account = BankAccount(100)
try:
    account.withdraw(150)
except InsufficientFundsError as e:
    print(f"Ошибка: {e}")

Иерархия исключений

В Python все исключения организованы в иерархию. Вот некоторые из наиболее распространенных встроенных исключений:

  • Exception: базовый класс для всех исключений
  • ArithmeticError: базовый класс для ошибок арифметики
  • ZeroDivisionError: деление на ноль
  • OverflowError: результат вычисления слишком велик
  • ValueError: некорректное значение
  • TypeError: операция с неподдерживаемым типом
  • IndexError: индекс вне диапазона
  • KeyError: обращение к несуществующему ключу словаря
  • FileNotFoundError: файл не найден
  • PermissionError: недостаточно прав для выполнения операции

Лучшие практики обработки исключений

Практические советы

  • Будьте конкретны: перехватывайте только те исключения, которые вы ожидаете и можете обработать.
  • Минимизируйте блоки try: помещайте в try только тот код, который может вызвать исключение.
  • Логируйте исключения: записывайте информацию об ошибках для последующего анализа.
  • Не игнорируйте исключения: пустой блок except: может скрыть серьезные проблемы.
  • Используйте finally: для гарантированного освобождения ресурсов.

LBYL vs EAFP

В Python существуют два подхода к предотвращению ошибок:

  • LBYL (Look Before You Leap) — "Смотри, прежде чем прыгать". Проверка условий перед выполнением операции.
  • EAFP (Easier to Ask Forgiveness than Permission) — "Проще просить прощения, чем разрешения". Выполнение операции в блоке try-except.
# Подход LBYL (проверка перед действием)
def get_dict_value_lbyl(dictionary, key):
    if key in dictionary:
        return dictionary[key]
    else:
        return None
        
# Подход EAFP (обработка исключения)
def get_dict_value_eafp(dictionary, key):
    try:
        return dictionary[key]
    except KeyError:
        return None

В Python чаще предпочитают стиль EAFP, поскольку он часто делает код более читаемым и избегает гонок условий (race conditions).

Практическое задание

Напишите функцию safe_divide(a, b), которая безопасно делит a на b, обрабатывая возможные исключения:

  • Если b равно 0, функция должна вернуть строку "Деление на ноль невозможно"
  • Если какой-либо из аргументов не является числом, функция должна вернуть "Ошибка: аргументы должны быть числами"
  • В случае успешного деления функция возвращает результат операции
Показать решение
def safe_divide(a, b):
    try:
        # Проверяем, что аргументы - числа
        result = float(a) / float(b)
        return result
    except ZeroDivisionError:
        return "Деление на ноль невозможно"
    except (ValueError, TypeError):
        return "Ошибка: аргументы должны быть числами"
        
# Тестирование функции
print(safe_divide(10, 2))      # 5.0
print(safe_divide(10, 0))      # Деление на ноль невозможно
print(safe_divide("10", "2"))  # 5.0
print(safe_divide("abc", 2))   # Ошибка: аргументы должны быть числами

Настройки

Цветовая схема

Тема