Класс, __init__, атрибуты экземпляра и методы
Содержание курса
В предыдущем уроке мы разобрали *args и **kwargs — гибкие сигнатуры функций, которые принимают произвольное количество аргументов. Этот же механизм скоро пригодится в конструкторах классов, но сначала нужно понять, что такое класс и как он устроен изнутри.
Определение класса и роль init
Класс в Python объявляется ключевым словом class, после которого идёт имя и двоеточие. По соглашению имена классов пишут в CamelCase — Counter, UserProfile, OrderItem. Тело класса — это обычный блок с отступом, внутри которого определяются методы.
class Counter:
pass
Пока это просто пустой класс. Он уже работает: Python создал новый тип Counter. Но чтобы объект при создании получал начальное состояние, нужен специальный метод __init__.
__init__ — это инициализатор экземпляра. Python вызывает его автоматически сразу после того, как создан новый объект. Важно: __init__ не выделяет память и не «конструирует» объект в низкоуровневом смысле — этим занимается __new__. Задача __init__ проще: получить только что созданный пустой объект и заполнить его данными.
Первый параметр __init__ — это self. Он ссылается на тот самый объект, который был только что создан. Именно через self метод знает, с каким объектом работает.
class Counter:
def __init__(self, name):
print(f"Создаётся объект Counter с именем {name}")
print(f"self — это объект по адресу: {id(self)}")
Когда Python выполняет Counter('A'), он:
- Создаёт новый объект типа
Counter. - Передаёт этот объект в
__init__какself. - Передаёт
'A'какname. - Выполняет тело
__init__.
Имя self — соглашение, а не синтаксическое требование. Python не проверяет, как называется первый параметр. Но отступать от этого соглашения не стоит: любой читающий код ожидает именно self.
Сам вызов Counter('A') выглядит как вызов функции, но это вызов класса как фабрики объектов. Python знает, что нужно создать экземпляр Counter и запустить __init__ с переданными аргументами. Аргумент для self не указывают явно — интерпретатор передаёт его сам.
class Counter:
def __init__(self, name):
print(f"__init__ вызван для объекта {id(self)}, name={name}")
c = Counter('A')
print(type(c)) # <class '__main__.Counter'>
После выполнения этого кода c — полноценный объект типа Counter. __init__ уже отработал. На следующем шаге мы запишем в self первые данные и превратим инициализатор в нечто полезное.
