Python. Классы
Время чтения: 7 минут
Прежде чем мы перейдём к использованию классов, я расскажу откуда классы взялись – про объектно-ориентированное программирование.
Объектно-ориентированное программирование (ООП) – подход к написанию программ при котором:
- Программа представляется в виде набора объектов и связей между ними
- Все объекты являются экземплярами классов
- Классы образуют иерархию наследования (про это в следующей статье)
Сейчас расскажу подробно что это всё значит.
Объект
В реальном мире мы с тобой постоянно взаимодействуем с какими-то объектами: стул, стол, кружка, телефон и т.д.
Как это всё может относиться к программированию?
До сих пор, твои программы представляли собой набор функций, которые вызывают друг друга в определённой последовательности (или просто одна функция). Этот подход называется функциональным – он хорошо себя показывает если нужно написать программу, которая выполняет только одну задачу.
Однако, если твоя программа делает сразу много задач, то тебе необходимо использовать ООП. С помощью этого подхода, ты сможешь представить свою программу как набор объектов, каждый из которых будет ответствененн только за свою область задач.
Вот пример простой программы “Список рецептов”, в которой можно только создавать, изменять и удалять рецепты:
Всё что я сделал – это перенёс функции программы по разным объектам.
Чтобы добавить ещё больше контекста – вот примеры задач, которые я мог бы решать в этой программе, как программист:
- Нужно чтобы теперь файл базы данных лежал в другой папке:
- Залезу в код объекта “База Данных”:
- Поменяю путь до файла
- Залезу в код объекта “База Данных”:
- Нужно увеличить размер кнопок в интерфейсе на 50%:
- Залезу в код объекта “Пользовательский Интерфейс”:
- Поменяю свойство размера кнопок
- Залезу в код объекта “Пользовательский Интерфейс”:
- Нужно добавить для рецепта новое численное поле “Время приготовления”:
- Залезу в код объекта “Пользовательский Интерфейс”:
- Добавлю новое численное поле ввода
- Подпишу его “Время приготовления”
- Добавлю дополнительный параметр cooking_duration в dict, который отправляю в объект “Список Рецептов”
- Залезу в код объекта “Список Рецептов”:
- Добавлю логику получения значения cooking_duration из данных от объекта “Пользовательский Интерфейс”
- Изменю запросы на добавление/изменение/удаление в объект “База Данных”, чтобы они учитывали новое значение cooking_duration
- Залезу в код объекта “Пользовательский Интерфейс”:
В этом случае ООП не убавило работы – мне нужно писать столько же кода, чтобы выполнить задачу. Однако, теперь весь этот код сгруппирован в объектах, и мне намного легче удержать в голове взаимосвязи между тремя объектами, нежели множественные вызовы между десятками функций.
Это я в общих чертах описал чем отличается ООП от функционального программирования, которым мы занимались до этого. Теперь рассмотрим классы.
Класс
Класс – это описание объекта, а объект – это существующий экземпляр класса.
То есть если мы описали какой-то класс, мы не можем его использовать по назначению – нельзя сесть на описание стула, можно сесть только на экземпляр стула.
Вот пример объявления класса, создания и использования объектов в Python:
# Создание класса
class Chair:
def __init__(self, legs, seat, back):
self.legs = legs
self.seat = seat
self.back = back
def take_a_seat(self):
print(f'Ты сел на стул у которого {self.legs} ножки, материал сидения - {self.seat}, а материал спинки - {self.back}')
# Создание объектов
wooden_chair = Chair(4, 'дерево', 'дерево')
plastic_chair = Chair(4, 'пластик', 'пластик')
metal_with_plastic_chair = Chair(3, 'металл', 'пластик')
# Вызов методов объектов
wooden_chair.take_a_seat()
plastic_chair.take_a_seat()
metal_with_plastic_chair.take_a_seat()
print(wooden_chair.compare_legs_count(4))
print(metal_with_plastic_chair.compare_legs_count(4))
# Обращение к полям объектов
legs = wooden_chair.legs
back = plastic_chair.back
seat = metal_with_plastic_chair.seat
print(f'Количество ножек деревянного стула: {legs}, материал спинки пластикового стула: {back}, материал сидения стула металл/пластик: {seat}')
metal_with_plastic_chair.legs = 4
print(f'Количество ножек деревянного стула: {legs}, материал спинки пластикового стула: {back}, материал сидения стула металл/пластик: {seat}')
Скопируй это код себе, и давай разберём его.
Объявление класса
class Chair:
def __init__(self, legs, seat, back):
self.legs = legs
self.seat = seat
self.back = back
def take_a_seat(self):
print(f'Ты сел на стул у которого {self.legs} ножки, материал сидения - {self.seat}, а материал спинки - {self.back}')
def compare_legs_count(self, legs_to_compare):
return self.legs == legs_to_compare:
В начале пишем имя класса, по которому мы в дальнейшем будем обращаться при создании объектов:
class Chair:
Затем идёт определение метода __init__ – этот метод также называется конструктором объекта.
Метод – это функция, которая объявлена внутри какого-то класса.
Используется именно такой странное название метода, потому что имя __init__ зарезервированно Python, и метод с таким именем автоматически вызывается при создании объекта.
В круглых скобках после названия как и в функции указываются параметры. В методах есть одна особенность – во всех методах класса первым параметром передаётся self. Именно через self мы будем обращаться к объекту и его полям.
Поле объекта – это переменная, которая хранится внутри этого объекта. У каждого объекта свой набор полей.
def __init__(self, legs, seat, back):
В методе __init__ мы создаём и заполняем поля legs, seat и back значениями, которые были переданы в параметрах конструктора.
# Поле legs -- хранит число ножек стула
self.legs = legs
# Поле seat -- хранит строку с названием материала сидения
self.seat = seat
# Поле back -- хранит строку с названием материала спинки
self.back = back
На этом логика конструктора заканчивается.
Кроме этого, мы добавляем метод take_a_seat
, который “симулирует” процесс восседания на стул. В нём просто выводятся значения полей объекта.
def take_a_seat(self):
print(f'Ты сел на стул у которого {self.legs} ножки, материал сидения - {self.seat}, а материал спинки - {self.back}')
И ещё один метод compare_legs_count
, который сравнивает количество ног стула с переданным через параметры. Я его добавил, чтобы показать как выглядят методы с параметрами.
def compare_legs_count(self, legs_to_compare):
return self.legs == legs_to_compare:
Создание объекта
Далее, мы создаём объекты – экземпляры класса Chair.
Для того, чтобы создать объект, надо написать имя класса, и в круглых скобках указать параметры, которые будут переданы в конструктор.
wooden_chair = Chair(4, 'дерево', 'дерево')
plastic_chair = Chair(4, 'пластик', 'пластик')
metal_with_plastic_chair = Chair(3, 'металл', 'пластик')
Таким образом в переменную запишется объект.
Окей, а что с ними делать?
Вызов методов класса
Помнишь мы создали методы take_a_seat
и compare_legs_count
? Так вот, мы можем их вызывать:
wooden_chair.take_a_seat()
# Вывод: Ты сел на стул у которого 4 ножки, материал сидения - дерево, а материал спинки - дерево
plastic_chair.take_a_seat()
# Вывод: Ты сел на стул у которого 4 ножки, материал сидения - пластик, а материал спинки - пластик
metal_with_plastic_chair.take_a_seat()
# Вывод: Ты сел на стул у которого 3 ножки, материал сидения - металл, а материал спинки - пластик
print(wooden_chair.compare_legs_count(4))
# Вывод: True
print(metal_with_plastic_chair.compare_legs_count(4))
# Вывод: False
Обрати внимание, что в зависимости от объекта, у нас получается разный вывод – мы же при создании объектов передавали разные параметры.
Мы никогда не передаём параметр self, так как Python сам его докидывает – ты же указывай всё остальное.
Обращение к полям класса
Чтобы обратиться к полю класса, надо написать “ОБЪЕКТ.ПОЛЕ”. С нимим можно работать как с обычными переменными, в них нет ничего особенного – просто они лежат в объекте.
# Можем вытаскивать значения из полей
legs = wooden_chair.legs
back = plastic_chair.back
seat = metal_with_plastic_chair.seat
print(f'Количество ножек деревянного стула: {legs}, материал спинки пластикового стула: {back}, материал сидения стула металл/пластик: {seat}')
# Можем присваивать
metal_with_plastic_chair.legs = 4
legs = wooden_chair.legs
print(f'Количество ножек деревянного стула: {legs}, материал спинки пластикового стула: {back}, материал сидения стула металл/пластик: {seat}')
Задание на закрепление
Создай класс Calculator:
- В нём должно быть поле “acc”, в которое в конструкторе класса присваивается 0
- В нём должны быть методы:
- add – принимает один параметр, складывает его с “acc” и кладёт результат в “acc”
- sub – принимает один параметр, вычитает его из “acc” и кладёт результат в “acc”
- mul – принимает один параметр, умножает его на “acc” и кладёт результат в acc
- div – принимает один параметр, делит “acc” на него и кладёт результат в acc
- clr – не принимает параметров, сбрасывает значение поля acc в 0
- res – не принимает параметров, возвращает значение поля acc
После того, как создашь класс Calculator, исполни этот код:
c = Calculator()
c.sub(10)
assert c.res() == -10
c.add(12)
assert c.res() == 2
c.add(4)
assert c.res() == 6
c.mul(2)
assert c.res() == 12
c.div(4)
assert c.res() == 3
c.clr()
assert c.res() == 0
Если посыпятся ошибки, то класс создан не по ТЗ :)
Заключение
Итого, мы изучили:
- Объектно-ориентированное программирование
- Объекты
- Классы (методы, конструктор, поля)
Теперь первые два пункта в определении ООП понятны:
Объектно-ориентированное программирование (ООП) – подход к написанию программ при котором:
- Программа представляется в виде набора объектов и связей между ними
- Все объекты являются экземплярами классов
- Классы образуют иерархию наследования
Если что – пиши, я помогу и постараюсь объяснить лучше.
Дальше мы залезем поглубже в принципы ООП – сложные, но полезные понятия.