Класс оборачивает данные и то, что ты с ними делаешь
Класс позволяет определить тип сущности - пользователя, банковский счёт, конфигурацию, запрос - и всё, что эта сущность умеет. Сама сущность - экземпляр класса. Действия называются методами.
Простейший возможный класс:
Разбираем:
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-программы используют классы скупо, предпочитая обычные функции и структуры данных там, где это возможно.