Python: уроки, справочник и задачи

ООП в Python для начинающих

Раздел: ООП в Python · Обновлено 31.10.2026

ООП в Python - это способ описывать программу через классы и объекты, которые хранят данные и умеют с ними работать. В статье на одном сквозном примере разобраны объявление класса, конструктор __init__, атрибуты класса и экземпляра, а также наследование, инкапсуляция и полиморфизм. Код рассчитан на Python 3 и запускается без дополнительных библиотек.

ООП в Python для начинающих
Фото: JIP / CC BY-SA 4.0, Wikimedia Commons
Содержание статьи
  1. Класс и объект
  2. Конструктор, атрибуты и методы
  3. Добавление, изменение и удаление атрибутов
  4. Инкапсуляция
  5. Наследование
  6. Полиморфизм
  7. Частые вопросы
    1. Зачем нужен self?
    2. Чем __init__ отличается от конструктора в других языках?
    3. Можно ли сделать атрибут по-настоящему приватным?
    4. Когда стоит использовать классы, а когда хватит функций?

Класс и объект

Класс описывает, какие данные будут у объектов и что они умеют делать. Объект (экземпляр) - конкретная сущность, созданная по этому описанию. Например, класс Student задает, что у студента есть имя и оценка, а объекты - это Тимур с оценкой 5 и Антон с оценкой 3.

Класс объявляется ключевым словом class. Имена классов по соглашению PEP 8 пишут в стиле CapWords: каждое слово с заглавной буквы, без подчеркиваний. Самый короткий класс состоит из одной инструкции pass:

class Student:
    pass

s = Student()       # создание объекта
print(type(s))      # <class '__main__.Student'>

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

Конструктор, атрибуты и методы

Метод __init__ вызывается автоматически при создании объекта и заполняет его начальными данными. Первый параметр любого обычного метода - self, ссылка на текущий объект. Через него метод обращается к атрибутам конкретного экземпляра.

class Student:
    count = 0  # атрибут класса, общий для всех

    def __init__(self, name, mark):
        self.name = name  # атрибуты экземпляра
        self.mark = mark
        Student.count += 1

    def info(self):
        return f"Имя: {self.name}, оценка: {self.mark}"


st1 = Student("Тимур", 5)
st2 = Student("Антон", 3)
print(st1.info())       # Имя: Тимур, оценка: 5
print(st2.info())       # Имя: Антон, оценка: 3
print(Student.count)    # 2

Обратите внимание на разницу между двумя видами атрибутов:

ВидГде задаетсяЧейПример
Атрибут классаВ теле классаОбщий для всех объектовStudent.count
Атрибут экземпляраЧерез self, обычно в __init__Свой у каждого объектаst1.name

Частая ошибка - хранить в атрибуте класса изменяемый объект, например список. Тогда все экземпляры будут дописывать данные в один общий список. Изменяемые значения создавайте в __init__ через self.

Добавление, изменение и удаление атрибутов

В Python объекту можно добавить атрибут уже после создания, изменить его или удалить оператором del:

st1.course = 2      # добавить атрибут
st1.course = 3      # изменить
del st1.course      # удалить

Когда имя атрибута известно только во время выполнения, например приходит из файла настроек, используют встроенные функции:

field = "mark"
print(getattr(st1, field))          # 5
setattr(st1, field, 4)
print(hasattr(st1, "course"))       # False
print(getattr(st1, "course", 1))    # 1

Имя атрибута передается строкой. Больше примеров с динамическим доступом приведено в материале про getattr.

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

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

Для проверки значений при записи используют свойства. Декоратор @property позволяет обращаться к методу как к обычному атрибуту, а сеттер проверяет данные:

class Student:
    def __init__(self, name, mark):
        self.name = name
        self.mark = mark  # сработает сеттер

    @property
    def mark(self):
        return self._mark

    @mark.setter
    def mark(self, value):
        if not 1 <= value <= 5:
            raise ValueError("Оценка должна быть от 1 до 5")
        self._mark = value


s = Student("Олег", 4)
s.mark = 7  # ValueError: Оценка должна быть от 1 до 5

Подробнее о геттерах и сеттерах написано в статье про property.

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

Наследование позволяет создать класс на основе существующего: потомок получает все атрибуты и методы родителя и может добавить свои или переопределить имеющиеся. Родитель указывается в скобках после имени класса, а функция super() вызывает его методы.

class Person:
    def __init__(self, name):
        self.name = name

    def info(self):
        return f"Имя: {self.name}"


class Student(Person):
    def __init__(self, name, mark):
        super().__init__(name)
        self.mark = mark

    def info(self):
        return f"{super().info()}, оценка: {self.mark}"


s = Student("Тимур", 5)
print(s.info())                 # Имя: Тимур, оценка: 5
print(isinstance(s, Person))    # True

Без вызова super().__init__() атрибут name не будет создан, и метод info() упадет с AttributeError. Все тонкости, включая порядок поиска методов, разобраны в статье про наследование классов.

Полиморфизм

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

class Teacher(Person):
    def info(self):
        return f"Преподаватель {self.name}"


people = [Student("Антон", 3), Teacher("Ирина Петровна")]
for p in people:
    print(p.info())
# Имя: Антон, оценка: 3
# Преподаватель Ирина Петровна

Цикл не знает, кто перед ним, и просто вызывает info(). Так же устроены встроенные функции: len() работает со списком, строкой и словарем, потому что у каждого из них есть метод __len__. Определив такие специальные методы в своем классе, вы научите объекты работать с операторами и встроенными функциями.

Частые вопросы

Ответы на вопросы, с которыми сталкиваются при первом знакомстве с ООП в Python.

Зачем нужен self?

Метод описан один раз в классе, а объектов может быть много. self указывает, с каким именно объектом работает метод. Python передает его автоматически: вызов st1.info() равносилен Student.info(st1).

Чем __init__ отличается от конструктора в других языках?

Объект создает метод __new__, а __init__ только инициализирует уже созданный объект. На практике __new__ переопределяют редко, поэтому __init__ обычно и называют конструктором.

Можно ли сделать атрибут по-настоящему приватным?

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

Когда стоит использовать классы, а когда хватит функций?

Класс оправдан, когда данные и операции над ними тесно связаны и создается несколько однотипных объектов: пользователи, товары, игровые персонажи. Для разовых преобразований данных обычно достаточно функций.