Суть объектно-ориентированного программирования (ООП) очевидно раскрывается в его названии. Эта парадигма предлагает представлять все компоненты программы как объекты из реальной жизни. У каждого такого объекта есть характеристики и он выполняет определенные функции.
Например, объект может представлять собой человека с такими характеристиками, как имя, возраст и род занятий, а также поведением, таким как ходьба, разговор и бег. Это Дима, ему 18 лет, он спортсмен и сейчас бегает по стадиону, а когда устанет, будет ходить и разговаривать с тренером о том, как повысить выносливость.
Python — язык с удобной, продуманной объектной моделью. Поэтому использовать эту парадигму на нем приятно. Давайте познакомимся с основными понятиями ООП Python.
Если самостоятельно не получается освоить язык Python, то лучше поискать хорошие курсы. Мы можем посоветовать проверенные курсы от наших друзей школы Mate Academy, Powercode, Hillel.
Основные принципы ООП включают абстракцию, инкапсуляцию, наследование и полиморфизм. Есть также объекты и классы. Вместе они составляют принцип работы любого объектно-ориентированного языка программирования, в том числе Python.
Инкапсуляция — это набор свойств и методов одного класса или объекта.
Программы могут быть очень длинными, в них может быть тонна движущихся частей. Через некоторое время становится трудно поддерживать все эти объекты, не создавая при этом сложности.
Чтобы управлять кодом было удобнее (и безопаснее), придумали инкапсуляцию. Это принцип, который призывает блокировать доступ к деталям сложных концепций.
Инкапсуляция позволяет скрыть конкретную информацию и контролировать доступ к внутреннему состоянию объекта. Общая идея этого механизма проста. Например, у вас есть атрибут, который не виден снаружи объекта. Вы связываете его с методами, обеспечивающими доступ для чтения или записи.
Если вы пьете кофе, то есть большая вероятность, что вы использовали кофемашину. Вы знаете, что кофемашина готовит кофе. Но вам не нужно знать, как она делает кофе. Все, что вам нужно знать, — какие кнопки нажимать, чтобы приготовить кофе. Вы используете интерфейс, определяемый кнопками, не беспокоясь о внутренней работе машины.
Рассматривайте метод как кофеварку, а ваш входной параметр — как кнопки на машине. Абстракция позволяет создавать бесшовные программы, просто зная, какой метод вызывать и какие параметры вводить.
Полиморфизм — способность одной функции выполняться по-разному. Это позволяет создавать несколько реализаций одной идеи. Например, вы делаете компьютерную игру. В ней есть класс «Транспорт» с методом «Двигаться». После выполнения метода самолет должен полететь, автомобиль — поехать, а корабль — поплыть. Все три действия — это движение, но реализованы они будут по-разному.
Полиморфизм можно применять двумя способами:
Когда класс имеет несколько методов с одинаковыми именами, но с другим набором параметров, это называется перегрузкой методов. Вы можете продолжить перегрузку метода, только если ваши методы удовлетворяют любому из следующих правил:
Второй способ полиморфизма — переопределение методов. Это возможно только в том случае, если с подкласс или родственный класс имеет тот же метод, что и родительский класс. Как и при перегрузке метода, существуют некоторые правила работы переопределения метода:
Наследование — возможность приобретать свойства существующих классов и создавать новые. Этот принцип призывает повторно использовать код без необходимости переписывать его в программе.
Вернемся к аналогии с компьютерной игрой. У вас есть абстракция «Транспорт». На этом уровне вы не учитывает характеристики каждого объекта: самолета, автомобиля, корабля.
Если приглядеться к объектам, то становится ясно, что у них есть характеристики, которые совпадают и которые различаются. Например, у самолета важен размах крыльев, а у автомобиля и корабля их вообще нет. При этом у самолета, автомобиля и корабля есть характеристика года выпуска или цвета.
Наследование позволяет сделать отдельный класс «Самолет», который будет наследником класса «Транспорт». Он сохраняет атрибуты класса «Транспорт» (год выпуска, цвет), но при этом получает новые — размах крыльев.
Объектно ориентированное программирование Python, как и других языков, основано на двух важных концепциях — классах и объектах.
Класс — это схема того, как что-то должно быть определено. На самом деле он не содержит никаких данных. Класс Cat указывает, что для определения кошки необходимы имя и возраст. Но он не содержит имя или возраст какой-либо конкретной кошки.
Создать класс очень просто. Достаточно написать инструкцию class
и добавить имя.
class FirstClass: pass
Для именования классов в Python используют нотацию CamelCase — каждое слово начинается с заглавной буквы.
В примере выше тело класса состоит из одного оператора pass.
Он нужен для того, чтобы интерпретатор Python не выдавал ошибку. Фактически это пустой класс, у которого нет ни атрибутов (характеристик), ни методов. Давайте их добавим.
Конструктор — это метод, который вызывается при создании классов. Благодаря ему у объектов изначально есть какие-то значения.
Например, на основании класса Cat можно создать объекты c именами Milo и Simba, а также возрастом — 2 и 3 года.
Создание объектов рассмотрим чуть позже, а пока вернемся к конструкторам.
В Python конструктором является метод __init__()
. В нем указываются все свойства, которыми должны обладать объекты класса Cat. Каждый раз, когда создается новый объект Cat, __init__()
устанавливает начальное состояние, присваивая свойствам значения. То есть __init__()
инициализирует каждый новый экземпляр класса.
Вы можете указать в __init__()
любое количество параметров, но первым параметром всегда будет переменная с именем self
. Когда создается новый экземпляр класса, он автоматически передается self
параметру в __init__(),
чтобы можно было определить новые атрибуты объекта.
Важно: self
— не зарезервированное имя. У первого параметра при инициализации может быть и другое название. Однако есть соглашение, что его называют self.
Это помогает унифицировать создание классов в разных проектах.
Пример:
class Cat: def __init__(self, name, age): self.name = name self.age = age
В теле __init__()
есть два оператора, использующих переменную self:
self.name = name
создает атрибут с именем name
и присваивает ему значение параметра name
.self.age = age
создает атрибут с именем age
и присваивает ему значение параметра age.
Важно соблюдать отступы. Они показывают, что метод __init__
принадлежит классу Cat, а атрибуты self.name
и self.age
принадлежат методу __init__.
Атрибуты, созданные в __init__(),
называются атрибутами экземпляра. Их значения зависят от конкретного экземпляра класса. У всех объектов есть имя и возраст. Но значения атрибутов name
и age
будут различаться в зависимости от объекта.
Чтобы не запутаться, давайте посмотрим примеры. Вернемся к нашему классу Cat. Вот он:
class Cat: def __init__(self, name, age): self.name = name self.age = age
Создадим на его основе два объекта — экземпляра класса Cat. Первый объект — кошка Milo. Ей два года. Второй объект — кошка Simba, ей три года. У обоих объектов есть имя и возраст. Но значения у них разные. Это достигается благодаря ключевому слову self
.
Self
— указатель на текущий экземпляр класса. Он позволяет работать с отдельными объектами, а не всеми экземплярами, которые принадлежат классу. Благодаря self
мы можем указать, что одну кошку зовут Milo, а вторую — Simba, что одной кошке — два года, а другой — три года.
Кроме атрибутов экземпляра существуют атрибуты класса. Они имеют одинаковое значение для всех объектов. Вы можете определить атрибут класса, присвоив значение имени переменной за пределами метода __init__().
Например:
class Cat: # Class attribute cute = "Cat is so cute" def __init__(self, name, age): self.name = name self.age = age
Атрибуты класса определяются под первой строкой имени класса и имеют отступ в четыре пробела. Им всегда должно быть присвоено начальное значение. При создании экземпляра класса автоматически создаются атрибуты класса, которым присваиваются их начальные значения.
Теперь вы можете создать двух кошек с разными именами и возрастом. Но их будет объединять атрибут класса cute
— обе они милые. Этот атрибут класса будет появляться во всех экземплярах класса, которые вы будете создавать.
Важно:
Используйте атрибуты класса для определения свойств, которые должны иметь одинаковое значение для каждого экземпляра класса. Используйте атрибуты экземпляра для свойств, которые меняются от одного экземпляра к другому.
Кроме __init__()
могут быть и другие методы. Они делятся на три группы:
Чтобы лучше понять разницу между ними, посмотрим пример:
class MyClass: def method(self): return 'instance method called', self @classmethod def classmethod(cls): return 'class method called', cls @staticmethod def staticmethod(): return 'static method called'
В этом классе есть все три метода. Первый — метод экземпляра класса. Он принимает параметр self
, но, как вы видели выше на примере атрибутов, ему можно передавать любые другие параметры.
Методы экземпляра класса — самые распространенные, обычно приходится работать именно с ними.
class Cat: def __init__(self, name, age): self.name = name self.age = age
Благодаря параметру self
методы экземпляра класса имеют доступ к атрибутам и методам объектов. Это позволяет изменять состояние объектов.
Второй тип — метод класса. В примере выше он определен с помощью декоратора @classmethod.
Декораторы — это обертки, которые позволяют менять назначение функций.
Методы класса принимают параметр cls.
При вызове метода этот параметр указывает не на объект, а на сам класс. Такие методы не могут менять объекты по отдельности — как вы теперь знаете, для этого нужен параметр self.
Но они могут менять состояние класса в целом, что влияет на все объекты этого класса.
Пример метода класса:
class Cat: def speak(cls): print('Мяу!')
Теперь вы можете создать экземпляры этого класса — конкретных кошек. Все они будут уметь говорить «Мяу».
Если вы измените метод класса, то это модифицирует всех кошек. Например:
class Cat: def speak(cls): print('Гав!')
Теперь все кошки будут лаять, а не мяукать. Пожалуй, лучше вернуть все как было.
Статический метод определен с помощью декоратора @staticmethod.
Он принимает любые параметры в любых количествах, кроме self
и cls.
Статические методы не меняют состояние ни класса, ни объекта. Они помещаются в класс для того, чтобы быть в пространстве имен, их используют для организационных целей.
В то время как класс является планом (схемой, чертежом — можно использовать разные сравнения), экземпляр представляет собой объект, созданный из класса и содержащий реальные данные. Экземпляр класса Cat — это уже не схема. Это настоящая кошка с именем и возрастом.
Класс похож на форму или анкету. Экземпляр подобен форме, которая была заполнена информацией. Подобно тому, как многие люди могут заполнять одну и ту же форму со своей собственной уникальной информацией, многие экземпляры могут быть созданы из одного класса.
Сначала нужно создать класс. На его основе вы будете создавать объекты — разные экземпляры класса.
Допустим, есть класс Cat. В нем определены параметры, которые должны быть у каждого объекта этого класса.
class Cat: cute = "Cat is so cute" def __init__(self, name, age): self.name = name self.age = age
Чтобы создать два объекта, достаточно передать имя и возраст — параметры, которые указаны в классе.
milo = Cat("Milo", 2) simba = Cat("Simba", 3)
Если в классе заданы параметры, а при создании объекта вы их не передаете, то вылезет ошибка. Например, вот этот код в данном случае не сработает:
milo = Cat()
В ответ появится сообщение об ошибке:
TypeError: __init__() missing 2 required positional arguments: 'name' and 'age'
Чтобы исправить ошибку, достаточно указать в круглых скобках имя и возраст. Тогда новый объект будет инициализирован корректно.
Есть и неочевидный момент. Метод класса __init__()
имеет три параметра. Почему же в примере ему передаются только два аргумента? Никакой магии нет. Когда вы создаете экземпляр объекта, Python создает новый объект и передает его первому параметру __init__().
Это по существу удаляет self,
поэтому вам нужно беспокоиться только о параметрах name и age.
После создания экземпляров вы можете получить доступ к их атрибутам, используя запись через точку. Например:
milo.name Output: “Milo” simba.age Output: 3
Аналогичным образом можно получить доступ к атрибутам класса, так как они применяются ко всем объектам.
milo.cute Output: "Cat is so cute"
Одним из самых больших преимуществ использования классов для организации данных является предсказуемость. Все экземпляры класса Cat имеют атрибуты .cute, .name .age
, поэтому вы можете уверенно их использовать, зная, что они всегда будут возвращать значение.
При этом атрибуты можно менять динамически. Например:
milo.age = 4 milo.age Output: 4
Ключевым выводом здесь является то, что пользовательские объекты по умолчанию изменяемы.
Мы рассмотрели основы объектно-ориентированного программирования в Python. Теперь вы знаете основные принципы и умеете создавать классы и объекты. Чтобы закрепить знания, посмотрите это видео про объектно-ориентированное программирование:
Прокси (proxy), или прокси-сервер — это программа-посредник, которая обеспечивает соединение между пользователем и интернет-ресурсом. Принцип…
Согласитесь, было бы неплохо соединить в одно сайт и приложение для смартфона. Если вы еще…
Повсеместное распространение смартфонов привело к огромному спросу на мобильные игры и приложения. Миллиарды пользователей гаджетов…
В перечне популярных чат-ботов с искусственным интеллектом Google Bard (Gemini) еще не пользуется такой популярностью…
Скрипт (англ. — сценарий), — это небольшая программа, как правило, для веб-интерфейса, выполняющая определенную задачу.…
Дедлайн (от англ. deadline — «крайний срок») — это конечная дата стачи проекта или задачи…