Перейти к содержимому

Объектно-ориентированное программирование и области видимости в Python. [1.9]

Объектно-ориентированное программирование и области видимости в Python. [1.9]

Введение в ООП

Объектно-ориентированное программирование (ООП) — это парадигма программирования, основанная на представлении программы как совокупности объектов, каждый из которых является экземпляром определенного класса. ООП позволяет моделировать реальные системы, делая код более структурированным, поддерживаемым и переиспользуемым.

Основные понятия ООП

  • Класс — шаблон или blueprint для создания объектов. Определяет атрибуты (данные) и методы (функции) объектов.
  • Объект (экземпляр) — конкретная реализация класса с собственными данными.
  • Атрибут — переменная, принадлежащая объекту или классу.
  • Метод — функция, определенная внутри класса и работающая с объектами этого класса.

Создание классов и объектов

В Python классы создаются с помощью ключевого слова class:

class Human:
    """
    Класс, представляющий человека.
    
    Атрибуты класса (общие для всех экземпляров):
        animal (str): Тип животного
        species (str): Вид
        bipedal (bool): Признак прямохождения
    
    Методы:
        speak(sentence): Выводит предложение
        walk(): Имитирует ходьбу
        eat(): Имитирует прием пищи
    """
    animal = 'Mammal'
    species = 'Homo Sapiens'
    bipedal = True

    def speak(self, sentence):
        """
        Выводит предложение на экран.
        
        Args:
            sentence (str): Предложение для вывода
        """
        print(sentence)

    def walk(self):
        """Имитирует ходьбу человека."""
        print("Человек идет")

    def eat(self):
        """Имитирует прием пищи."""
        print("Человек ест")

# Создание экземпляров класса
john = Human()
elisabeth = Human()

# Вызов методов
john.speak("Привет! Меня зовут Джон!")
elisabeth.speak("Привет! Меня зовут Элизабет!")
john.walk()
# Вывод:
# Привет! Меня зовут Джон!
# Привет! Меня зовут Элизабет!
# Человек идет

Важно: Первый параметр любого метода класса — self. Это ссылка на текущий экземпляр класса. При вызове метода Python автоматически передает экземпляр в качестве первого аргумента.

Конструктор __init__

Конструктор (__init__) — специальный метод, который автоматически вызывается при создании нового объекта. Он используется для инициализации атрибутов экземпляра.

class Person:
    """
    Класс, представляющий человека с индивидуальными характеристиками.
    
    Атрибуты экземпляра:
        name (str): Имя человека
        date_of_birth (str): Дата рождения в формате ДД.ММ.ГГГГ
        height (float): Рост в метрах
        weight (float): Вес в килограммах
    """
    
    def __init__(self, name, date_of_birth, height, weight):
        """
        Инициализирует новый экземпляр класса Person.
        
        Args:
            name (str): Имя человека
            date_of_birth (str): Дата рождения
            height (float): Рост в метрах
            weight (float): Вес в килограммах
        """
        self.name = name
        self.date_of_birth = date_of_birth
        self.height = height
        self.weight = weight
    
    def get_bmi(self):
        """
        Рассчитывает индекс массы тела (ИМТ).
        
        Returns:
            float: Значение ИМТ
        """
        return self.weight / (self.height ** 2)
    
    def __str__(self):
        """
        Возвращает строковое представление объекта.
        
        Returns:
            str: Информация о человеке
        """
        return f"{self.name}, рост: {self.height}м, вес: {self.weight}кг"

# Создание объекта с использованием конструктора
anna = Person("Анна", "15.05.1990", 1.68, 58.5)
print(anna)  # Анна, рост: 1.68м, вес: 58.5кг
print(f"ИМТ Анны: {anna.get_bmi():.1f}")  # ИМТ Анны: 20.7

Столпы ООП

1. Абстракция

Абстракция — это процесс выделения существенных характеристик объекта и игнорирования несущественных деталей. Абстракция позволяет создавать простые модели сложных систем.

from abc import ABC, abstractmethod

class Figure(ABC):
    """
    Абстрактный базовый класс для геометрических фигур.
    
    Абстрактные методы (должны быть реализованы в дочерних классах):
        area(): Вычисляет площадь фигуры
        perimeter(): Вычисляет периметр фигуры
    """
    
    @abstractmethod
    def area(self):
        """Абстрактный метод для вычисления площади."""
        pass
    
    @abstractmethod
    def perimeter(self):
        """Абстрактный метод для вычисления периметра."""
        pass

class Circle(Figure):
    """
    Класс, представляющий круг.
    
    Атрибуты:
        radius (float): Радиус круга
    """
    
    def __init__(self, radius):
        self.radius = radius
    
    def area(self):
        """Вычисляет площадь круга."""
        return 3.14 * self.radius ** 2
    
    def perimeter(self):
        """Вычисляет длину окружности."""
        return 2 * 3.14 * self.radius

class Rectangle(Figure):
    """
    Класс, представляющий прямоугольник.
    
    Атрибуты:
        width (float): Ширина прямоугольника
        height (float): Высота прямоугольника
    """
    
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def area(self):
        """Вычисляет площадь прямоугольника."""
        return self.width * self.height
    
    def perimeter(self):
        """Вычисляет периметр прямоугольника."""
        return 2 * (self.width + self.height)

# Использование абстрактного класса
shapes = [Circle(5), Rectangle(4, 6)]

for shape in shapes:
    print(f"Площадь: {shape.area():.2f}, Периметр: {shape.perimeter():.2f}")
# Площадь: 78.50, Периметр: 31.40
# Площадь: 24.00, Периметр: 20.00

Пояснение: ABC (Abstract Base Class) — это базовый класс для создания абстрактных классов. Абстрактные методы помечаются декоратором @abstractmethod и должны быть реализованы во всех дочерних классах.

2. Инкапсуляция

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

  • _var — защищенный (protected) атрибут/метод (соглашение, что не следует использовать вне класса)
  • __var — приватный (private) атрибут/метод (Python изменяет имя для сокрытия)
class BankAccount:
    """
    Класс банковского счета с инкапсуляцией данных.
    
    Атрибуты:
        _account_number (str): Защищенный номер счета
        __balance (float): Приватный баланс счета
        owner (str): Владелец счета
    """
    
    def __init__(self, account_number, balance, owner):
        self._account_number = account_number  # Защищенный атрибут
        self.__balance = balance  # Приватный атрибут
        self.owner = owner
    
    def deposit(self, amount):
        """
        Пополняет счет на указанную сумму.
        
        Args:
            amount (float): Сумма пополнения
            
        Returns:
            bool: Успешность операции
        """
        if amount <= 0:
            print("Сумма должна быть положительной")
            return False
        self.__balance += amount
        return True
    
    def withdraw(self, amount):
        """
        Снимает деньги со счета.
        
        Args:
            amount (float): Сумма для снятия
            
        Returns:
            bool: Успешность операции
        """
        if amount <= 0:
            print("Сумма должна быть положительной")
            return False
        if amount > self.__balance:
            print("Недостаточно средств")
            return False
        self.__balance -= amount
        return True
    
    def get_balance(self):
        """
        Возвращает текущий баланс.
        
        Returns:
            float: Текущий баланс
        """
        return self.__balance
    
    def __str__(self):
        """Возвращает строковое представление счета."""
        return f"Счет {self._account_number[-4:]} владельца {self.owner}"

# Использование инкапсуляции
account = BankAccount("RU1234567890", 1000.0, "Иван Петров")

print(account)  # Счет 7890 владельца Иван Петров
account.deposit(500.0)
print(f"Баланс: {account.get_balance()}")  # Баланс: 1500.0

# Попытка прямого доступа к приватному атрибуту
# print(account.__balance)  # AttributeError: 'BankAccount' object has no attribute '__balance'
# print(account._BankAccount__balance)  # 1500.0 (обход инкапсуляции, но не рекомендуется)

Важно: В Python инкапсуляция реализована на уровне соглашений, а не строгого синтаксиса. Приватные атрибуты можно получить через _ClassName__attribute, но это нарушает принципы инкапсуляции.

3. Наследование

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

class Animal:
    """
    Базовый класс для всех животных.
    
    Атрибуты:
        name (str): Имя животного
        species (str): Вид животного
    """
    
    def __init__(self, name, species):
        self.name = name
        self.species = species
    
    def make_sound(self):
        """Базовый метод для издавания звука."""
        print(f"{self.name} издает звук")
    
    def __str__(self):
        return f"{self.name} ({self.species})"

class Dog(Animal):
    """
    Класс собаки, наследующий от Animal.
    
    Атрибуты:
        breed (str): Порода собаки
    """
    
    def __init__(self, name, breed):
        # Вызов конструктора родительского класса
        super().__init__(name, "Собака")
        self.breed = breed
    
    def make_sound(self):
        """Переопределенный метод для лая собаки."""
        print(f"{self.name} гавкает: Гав-гав!")
    
    def fetch(self):
        """Специфический метод для собаки."""
        print(f"{self.name} приносит палку")

class Cat(Animal):
    """
    Класс кошки, наследующий от Animal.
    
    Атрибуты:
        color (str): Цвет кошки
    """
    
    def __init__(self, name, color):
        super().__init__(name, "Кошка")
        self.color = color
    
    def make_sound(self):
        """Переопределенный метод для мяуканья кошки."""
        print(f"{self.name} мяукает: Мяу!")
    
    def scratch(self):
        """Специфический метод для кошки."""
        print(f"{self.name} царапает диван")

# Создание объектов
dog = Dog("Рекс", "Овчарка")
cat = Cat("Мурка", "Серый")

animals = [dog, cat]

for animal in animals:
    print(animal)
    animal.make_sound()
    
    # Проверка типа для вызова специфических методов
    if isinstance(animal, Dog):
        animal.fetch()
    elif isinstance(animal, Cat):
        animal.scratch()

# Вывод:
# Рекс (Собака)
# Рекс гавкает: Гав-гав!
# Рекс приносит палку
# Мурка (Кошка)
# Мурка мяукает: Мяу!
# Мурка царапает диван

Пояснение:

  • super() — позволяет вызывать методы родительского класса
  • isinstance() — проверяет, является ли объект экземпляром указанного класса
  • Переопределение методов (make_sound) позволяет изменять поведение в дочерних классах

4. Полиморфизм

Полиморфизм — это возможность использовать объекты разных классов через единый интерфейс. Благодаря полиморфизму один и тот же метод может вести себя по-разному для разных объектов.

class Shape:
    """Абстрактный базовый класс для геометрических фигур."""
    
    def area(self):
        """Абстрактный метод для вычисления площади."""
        raise NotImplementedError("Метод area() должен быть реализован в дочернем классе")
    
    def __str__(self):
        """Возвращает строковое представление фигуры."""
        return self.__class__.__name__

class Square(Shape):
    """Класс квадрата."""
    
    def __init__(self, side):
        self.side = side
    
    def area(self):
        """Вычисляет площадь квадрата."""
        return self.side ** 2

class Triangle(Shape):
    """Класс треугольника."""
    
    def __init__(self, base, height):
        self.base = base
        self.height = height
    
    def area(self):
        """Вычисляет площадь треугольника."""
        return 0.5 * self.base * self.height

class Circle(Shape):
    """Класс круга."""
    
    def __init__(self, radius):
        self.radius = radius
    
    def area(self):
        """Вычисляет площадь круга."""
        return 3.14 * self.radius ** 2

# Полиморфизм в действии
shapes = [
    Square(5),
    Triangle(4, 6),
    Circle(3)
]

for shape in shapes:
    print(f"{shape}: площадь = {shape.area():.2f}")
# Вывод:
# Square: площадь = 25.00
# Triangle: площадь = 12.00
# Circle: площадь = 28.26

Важно: Полиморфизм позволяет работать с объектами разных классов через единый интерфейс (area()), не зная их конкретного типа во время выполнения.

5. Композиция

Композиция — это механизм создания сложных объектов путем объединения более простых. В отличие от наследования, композиция основана на отношении "имеет" (has-a), а не "является" (is-a).

class Engine:
    """
    Класс двигателя автомобиля.
    
    Атрибуты:
        power (int): Мощность двигателя в лошадиных силах
        fuel_type (str): Тип топлива
    """
    
    def __init__(self, power, fuel_type="Бензин"):
        self.power = power
        self.fuel_type = fuel_type
    
    def start(self):
        """Запускает двигатель."""
        print(f"Двигатель мощностью {self.power} л.с. запущен на {self.fuel_type}")
    
    def stop(self):
        """Останавливает двигатель."""
        print("Двигатель остановлен")

class Wheels:
    """
    Класс колес автомобиля.
    
    Атрибуты:
        count (int): Количество колес
        size (str): Размер колес
    """
    
    def __init__(self, count=4, size="R16"):
        self.count = count
        self.size = size
    
    def rotate(self):
        """Имитирует вращение колес."""
        print(f"Все {self.count} колеса размера {self.size} вращаются")

class Car:
    """
    Класс автомобиля, использующий композицию.
    
    Атрибуты:
        engine (Engine): Двигатель автомобиля
        wheels (Wheels): Колеса автомобиля
        brand (str): Марка автомобиля
        model (str): Модель автомобиля
    """
    
    def __init__(self, brand, model, engine_power, wheel_count=4, wheel_size="R16"):
        # Создание компонентов через композицию
        self.engine = Engine(engine_power)
        self.wheels = Wheels(wheel_count, wheel_size)
        self.brand = brand
        self.model = model
    
    def start(self):
        """Запускает автомобиль."""
        print(f"Запуск автомобиля {self.brand} {self.model}")
        self.engine.start()
        self.wheels.rotate()
        print("Автомобиль готов к движению")
    
    def stop(self):
        """Останавливает автомобиль."""
        print(f"Остановка автомобиля {self.brand} {self.model}")
        self.engine.stop()
        print("Автомобиль остановлен")

# Использование композиции
my_car = Car("Toyota", "Camry", 200)
my_car.start()
my_car.stop()

# Вывод:
# Запуск автомобиля Toyota Camry
# Двигатель мощностью 200 л.с. запущен на Бензин
# Все 4 колеса размера R16 вращаются
# Автомобиль готов к движению
# Остановка автомобиля Toyota Camry
# Двигатель остановлен
# Автомобиль остановлен

Преимущества композиции перед наследованием:

  • Гибкость: можно легко заменить компоненты
  • Повторное использование кода: одни и те же компоненты можно использовать в разных классах
  • Упрощение иерархии классов
  • Легче тестировать отдельные компоненты

Области видимости переменных

Область видимости определяет, где в программе можно использовать переменную. В Python существует несколько типов областей видимости, которые следуют правилу LEGB:

  • Local (локальная) — внутри функции
  • Enclosing (обрамляющая) — вложенные функции
  • Global (глобальная) — на уровне модуля
  • Built-in (встроенная) — встроенные имена Python

Глобальная область видимости

Переменные, объявленные на уровне модуля (вне функций и классов), имеют глобальную область видимости.

# Глобальные переменные
global_var = "Я глобальная переменная"
PI = 3.14159  # Константы обычно пишутся заглавными буквами

def access_global():
    """Функция может читать глобальные переменные."""
    print(f"В функции: {global_var}")
    print(f"Число PI: {PI}")

def modify_global():
    """Для изменения глобальной переменной нужно использовать ключевое слово global."""
    global global_var
    global_var = "Я измененная глобальная переменная"
    print(f"Измененная в функции: {global_var}")

# Использование
access_global()
# В функции: Я глобальная переменная
# Число PI: 3.14159

modify_global()
print(f"После изменения: {global_var}")
# Измененная в функции: Я измененная глобальная переменная
# После изменения: Я измененная глобальная переменная

Локальная область видимости

Переменные, объявленные внутри функции, имеют локальную область видимости и доступны только внутри этой функции.

def local_scope_example():
    """Демонстрация локальной области видимости."""
    local_var = "Я локальная переменная"
    print(local_var)
    
    # Вложенные блоки (if, for, while) не создают новую область видимости
    if True:
        nested_var = "Я в локальной области, но в блоке if"
        print(nested_var)
    
    # nested_var доступна вне блока if, но только внутри функции
    print(f"Все еще доступна: {nested_var}")

local_scope_example()
# print(local_var)  # NameError: name 'local_var' is not defined

Область видимости классов

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

class ScopeExample:
    """Класс для демонстрации областей видимости."""
    
    class_var = "Атрибут класса"  # Доступен всем экземплярам
    
    def __init__(self, instance_var):
        self.instance_var = instance_var  # Атрибут экземпляра
    
    def method(self):
        """Метод с доступом к разным областям видимости."""
        local_var = "Локальная переменная метода"
        print(f"В методе:")
        print(f"  - Атрибут класса: {self.class_var}")
        print(f"  - Атрибут экземпляра: {self.instance_var}")
        print(f"  - Локальная переменная: {local_var}")
    
    @classmethod
    def class_method(cls):
        """Метод класса имеет доступ к атрибутам класса."""
        print(f"В методе класса: {cls.class_var}")
        # print(cls.instance_var)  # AttributeError: type object 'ScopeExample' has no attribute 'instance_var'
    
    @staticmethod
    def static_method():
        """Статический метод не имеет доступа к атрибутам экземпляра или класса."""
        print("В статическом методе")
        # print(self.class_var)  # NameError: name 'self' is not defined
        # print(cls.class_var)   # NameError: name 'cls' is not defined

# Создание экземпляра
obj = ScopeExample("Атрибут экземпляра")

# Доступ к атрибутам
print(f"Вне класса:")
print(f"  - Атрибут класса: {ScopeExample.class_var}")
print(f"  - Атрибут экземпляра: {obj.instance_var}")

# Вызов методов
obj.method()
ScopeExample.class_method()
ScopeExample.static_method()

# Попытка доступа к локальной переменной извне
# print(local_var)  # NameError: name 'local_var' is not defined

Вложенные функции и nonlocal

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

def outer_function():
    """Внешняя функция с вложенной функцией."""
    outer_var = "Переменная внешней функции"
    
    def inner_function():
        """Вложенная функция."""
        nonlocal outer_var  # Указываем, что используем переменную из внешней области
        outer_var = "Измененная переменная внешней функции"
        print(f"Во вложенной функции: {outer_var}")
    
    print(f"До вызова вложенной функции: {outer_var}")
    inner_function()
    print(f"После вызова вложенной функции: {outer_var}")

outer_function()
# Вывод:
# До вызова вложенной функции: Переменная внешней функции
# Во вложенной функции: Измененная переменная внешней функции
# После вызова вложенной функции: Измененная переменная внешней функции

Чек-лист по статье 1.9

Объектно-ориентированное программирование

  • Я понимаю разницу между классом и объектом (экземпляром)
  • Я могу создавать классы с атрибутами и методами
  • Я знаю, как использовать конструктор __init__ для инициализации объектов
  • Я понимаю назначение ключевого слова self в методах
  • Я могу создавать абстрактные базовые классы с помощью ABC и @abstractmethod
  • Я понимаю принцип инкапсуляции и знаю соглашения об именовании (_var, __var)
  • Я могу создавать дочерние классы с помощью наследования
  • Я умею использовать super() для вызова методов родительского класса
  • Я понимаю концепцию полиморфизма и могу применять ее на практике
  • Я знаю разницу между наследованием и композицией
  • Я могу создавать классы с использованием композиции
  • Я понимаю, когда использовать наследование, а когда композицию

Области видимости

  • Я знаю правило LEGB (Local, Enclosing, Global, Built-in)
  • Я могу объявлять и использовать глобальные переменные
  • Я понимаю, когда нужно использовать ключевое слово global
  • Я знаю, что локальные переменные доступны только внутри функции
  • Я понимаю разницу между атрибутами класса и атрибутами экземпляра
  • Я знаю, как работают методы класса (@classmethod) и статические методы (@staticmethod)
  • Я понимаю концепцию вложенных функций и знаю, когда использовать nonlocal
  • Я могу объяснять цепочку поиска переменных в Python
  • Я понимаю, почему глобальные переменные следует использовать осторожно
  • Я знаю, как избегать конфликтов имен в разных областях видимости

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

  • Я могу документировать классы и методы с помощью docstrings
  • Я умею создавать строковые представления объектов с помощью __str__
  • Я могу проверять типы объектов с помощью isinstance() и type()
  • Я понимаю, как работать с приватными атрибутами и когда их использовать
  • Я могу создавать иерархии классов с использованием наследования
  • Я умею переопределять методы в дочерних классах
  • Я понимаю, как создавать интерфейсы с помощью абстрактных классов
  • Я могу проектировать системы с использованием композиции вместо наследования
  • Я знаю, как управлять областями видимости для написания чистого кода
  • Я понимаю, как избегать побочных эффектов при работе с глобальными переменными

Заключение

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

Ключевые принципы ООП (абстракция, инкапсуляция, наследование, полиморфизм и композиция) работают вместе, создавая мощную парадигму для разработки программного обеспечения. Понимание областей видимости (глобальной, локальной, классовой и вложенных) помогает избежать многих распространенных ошибок и делает код более чистым.

В следующих статьях мы углубимся в более продвинутые темы 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

Пример 1: Генерация встроенных исключений
Python
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)
Пример 2: Повторный выброс исключения
Python
def process_file(filename):
    try:
        with open(filename, 'r') as file:
            return file.read()
    except PermissionError:
        print(f"Нет доступа к файлу {filename}")
        raise  # Повторно выбрасываем то же исключение
Пример 3: Замена типа исключения
Python
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:
Информативные сообщения — всегда добавляйте понятное описание ошибки
Документируйте исключения — указывайте в docstring, какие исключения могут быть выброшены
Освобождайте ресурсы — используйте finally или контекстные менеджеры (with)
Свои исключения для бизнес-логики — создавайте пользовательские исключения для специфичных ситуаций

Шпаргалка по обработке исключений

Python
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("Завершение операции")

Примечание: Это приложение является дополнением к основной статье об условных операторах, циклах и исключениях. Здесь собрана справочная информация для быстрого доступа и повторения материала.

Ссылки по теме

Конспект:
Суббота, 06 декабря 2025
Объектно-ориентированное программирование и области видимости в Python. [1.9]