Python
Классы в Python: простой пример
Класс в Python описывает объект: какие данные он хранит и какие действия умеет выполнять. Если функция — это именованное действие, то класс объединяет данные и действия вокруг одной сущности
Проще всего начать с задачи. Например, у нас есть задача в списке дел. У нее есть название, статус выполнения и действия: отметить выполненной, переименовать, вернуть текстовое описание
По этой теме полезно отдельно посмотреть Составьте еженедельное расписание занятий, чтобы расширить контекст и сравнить подходы
По этой теме полезно отдельно посмотреть Аннотации типов для декораторов в Python, чтобы расширить контекст и сравнить подходы
Первый класс
class Task:
def __init__(self, title):
self.title = title
self.done = False
task = Task('написать код')
print(task.title)
print(task.done)
Результат:
написать код
False
Task — это класс. task — объект, созданный по этому классу. Один класс можно использовать для создания многих объектов
Что делает __init__
Метод __init__ вызывается при создании объекта
task = Task('написать код')
Python создает новый объект и передает значение 'написать код' в параметр title. Внутри __init__ мы сохраняем это значение в self.title
self.title = title
self.done = False
self.title и self.done — атрибуты объекта. Они принадлежат конкретной задаче
Зачем нужен self
self — это ссылка на текущий объект. Через self метод получает доступ к данным именно этого объекта
class Task:
def __init__(self, title):
self.title = title
self.done = False
def describe(self):
return f'{self.title}: {self.done}'
task = Task('написать код')
print(task.describe())
Результат:
написать код: False
Когда вы вызываете task.describe(), Python сам передает объект task в параметр self
Метод, который меняет объект
Добавим действие mark_done()
class Task:
def __init__(self, title):
self.title = title
self.done = False
def mark_done(self):
self.done = True
def describe(self):
status = 'готово' if self.done else 'в работе'
return f'{self.title}: {status}'
task = Task('написать код')
print(task.describe())
task.mark_done()
print(task.describe())
Результат:
написать код: в работе
написать код: готово
Метод mark_done() меняет состояние объекта. После вызова task.done становится True
Несколько объектов одного класса
Класс — это шаблон. Объект — конкретный экземпляр
task1 = Task('написать код')
task2 = Task('запустить тесты')
task1.mark_done()
print(task1.describe())
print(task2.describe())
Результат:
написать код: готово
запустить тесты: в работе
У task1 и task2 свои значения title и done. Изменение одного объекта не меняет другой
Список объектов
Объекты можно хранить в списке
tasks = [
Task('написать код'),
Task('запустить тесты'),
Task('опубликовать'),
]
tasks[0].mark_done()
for task in tasks:
print(task.describe())
Такой подход уже похож на маленькую модель данных для приложения. Вместо разрозненных словарей у каждой задачи есть понятные методы
Метод rename
Добавим переименование
class Task:
def __init__(self, title):
self.title = title
self.done = False
def mark_done(self):
self.done = True
def rename(self, new_title):
self.title = new_title
def describe(self):
status = 'готово' if self.done else 'в работе'
return f'{self.title}: {status}'
task = Task('старое название')
task.rename('новое название')
print(task.describe())
Методы нужны не только для вывода. Они помогают менять объект понятным способом
Когда класс полезен
Класс стоит использовать, когда у сущности есть несколько связанных данных и действий
Хорошие примеры:
- пользователь с именем, ролью и методом проверки прав
- задача с названием, статусом и методом завершения
- корзина с товарами и методом подсчета суммы
- API-клиент с настройками и методом запроса
- игра с персонажем, здоровьем и действиями
Если у вас одна функция и одно значение, класс может быть лишним. В Python нормально начинать с функции или словаря, а класс добавлять тогда, когда данных и поведения становится больше
Частые ошибки
Забыть self в методе
class Task:
def describe():
return 'задача'
Правильно:
class Task:
def describe(self):
return 'задача'
Обращаться к локальной переменной вместо атрибута
def __init__(self, title):
title = title
Правильно:
def __init__(self, title):
self.title = title
Создавать класс ради одной строки, где достаточно функции
Хранить все в глобальных переменных и не использовать состояние объекта
Делать слишком большой класс, который отвечает сразу за данные, файлы, сеть, базу и интерфейс
Практические задачи
Добавьте метод is_done(), который возвращает True или False
Добавьте поле priority
Создайте список из трех задач и выведите только незавершенные
Сделайте класс User с полями name и role
Добавьте методу describe() более понятный текстовый статус
Переделайте словарь задачи в класс Task
Класс в Python помогает объединить данные и действия вокруг одной сущности. __init__ задает начальное состояние, self указывает на текущий объект, методы читают или меняют данные объекта
Начинайте с маленьких классов. Если объект можно описать одной фразой и у него есть понятные действия, класс может сделать код чище. Если класс появляется только ради красоты, лучше оставить функцию или словарь