Menu

Классы в Python: __init__, self, методы, наследование и dataclasses

Введение в классы Python для начинающих - определение, создание экземпляров, метод __init__, наследование и когда вместо обычного класса брать dataclass.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Класс оборачивает данные и то, что ты с ними делаешь

Класс позволяет определить тип сущности - пользователя, банковский счёт, конфигурацию, запрос - и всё, что эта сущность умеет. Сама сущность - экземпляр класса. Действия называются методами.

Простейший возможный класс:

Разбираем:

  • class User: открывает определение класса. Имена классов по соглашению в PascalCase.
  • __init__ - инициализатор. Он вызывается автоматически при создании нового экземпляра и задаёт атрибуты через self.whatever = ....
  • greet - метод. self - то, через что метод обращается к экземпляру, на котором его позвали.
  • User("Rosa", "rosa@example.com") создаёт экземпляр. Python вызывает __init__ с self, равным новому экземпляру, и остальными аргументами.
  • user.greet() вызывает метод. Python передаёт user как self автоматически.

self - просто первый аргумент

self - не ключевое слово. Это имя, которое Python даёт первому параметру методов экземпляра, - самого экземпляра, на котором метод работает. Формально можно назвать как угодно; все используют self, потому что все используют self.

Каждый метод экземпляра берёт self первым параметром. Так метод знает, из какого экземпляра читать и что менять.

Добавляем поведение

Методы умеют читать и изменять состояние:

__repr__ - ещё один специальный метод, хук «как мне показаться при печати». Хорошая реализация окупается во время отладки.

Атрибуты класса vs атрибуты экземпляра

Атрибуты, определённые в теле класса (вне __init__), делятся между всеми экземплярами. Атрибуты, присваиваемые self, - у каждого свои:

Используй атрибуты класса для по-настоящему общих значений - констант, счётчиков, конфигов. Всё, что специфично экземпляру, - в __init__.

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

Класс может наследовать другой, получая его методы и атрибуты даром, и добавлять или переопределять по нужде:

super() даёт доступ к родителю - часто используется внутри __init__, чтобы инициализировать родительские атрибуты перед добавлением своих:

Наследование полезно, но легко пере-использовать. Начинающий питонист часто тянется к нему, когда композиция - держать объект как атрибут - понятнее. Начинай с плоской структуры классов и вводи наследование только при настоящем отношении «is-a».

Dataclasses: более приятный дефолт для записей

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

Сравни с ручной записью __init__, __repr__ и __eq__. Dataclass - правильный первый выбор, когда иначе ты написал бы class Thing только с __init__, проставляющим атрибуты.

Property: вычисляемые атрибуты

Иногда хочется «атрибут», который на самом деле вычисляется. @property делает это похожим на обращение к атрибуту:

Используй property, когда ты написал бы obj.get_something(), - замена на property делает API естественнее. Не прячь за ним дорогие операции: пользователь ожидает, что обращение к атрибуту - быстрое.

Приватное по соглашению

В Python нет настоящей приватности. По соглашению атрибуты с ведущим подчёркиванием предполагаются приватными - читатель не должен трогать их снаружи класса:

class Cache:
    def __init__(self):
        self._store = {}

    def put(self, key, value):
        self._store[key] = value

    def get(self, key, default=None):
        return self._store.get(key, default)

Имена с двойным подчёркиванием (__name) включают name mangling: к атрибуту добавляется имя класса. Это инструмент против коллизий при наследовании, а не защита.

Когда класс не нужен

Если у тебя просто набор связанных данных - сойдёт dataclass, обычный словарь или namedtuple. Если у тебя одна функция - ей не нужен класс-обёртка. Тянись к классам, когда у тебя и состояние, и поведение связаны вместе - особенно если будет много экземпляров одного рода.

Финальный пример

Маленький файло-подобный лог-буфер:

Обрати внимание на __len__ - ещё один магический метод. Его определение делает len(logger) работающим, потому что len(x) - это на самом деле вызов x.__len__(). В Python десятки таких хуков; подбирай по мере надобности.

Дальше: генераторы

Классы связывают данные и поведение. Следующая глава смотрит на другой вид абстракции - итерацию - начиная с генераторов: функций, которые выдают значения по одному, делая паузы между ними. Они прекрасно сочетаются с оператором with и контекстными менеджерами, которые встретим сразу после.

Часто задаваемые вопросы

Что такое класс в Python?

Класс - это чертёж для создания объектов, собирающих данные (атрибуты) и поведение (методы) вместе. class User: определяет класс User; User(...) создаёт экземпляр. Классы - главный инструмент Python для моделирования вещей, у которых есть и состояние, и поведение.

Что значит self в Python?

self - первый параметр каждого метода экземпляра. Когда ты вызываешь user.greet(), Python передаёт экземпляр user как self. Внутри метода self.name обращается к атрибуту этого пользователя. Имя self - соглашение, не ключевое слово, но его использует каждый питонист.

Python - объектно-ориентированный язык?

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

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ