Объектно-ориентированное программирование в Python
В этой статье мы познакомимся с парадигмой объектно-ориентированного программирования (ООП) и его фундаментальными принципами.
ООП
Python — мультипарадигмальный язык программирования. Он поддерживает разные подходы к программированию.
Один из популярных подходов к решению проблем — создание объектов. Это называется объектно-ориентированным программированием (ООП).
У объекта есть две характеристики:
- атрибуты;
- поведение.
Рассмотрим пример. Допустим, наш объект — это попугай. У попугая есть такие свойства:
- Имя, возраст, цвет. Это атрибуты.
- То, как попугай поет и танцует. Это поведение.
ООП предлагает писать код, который можно использовать повторно. Такой принцип называется DRY (don’t repeat yourself, «не повторяйся»).
Класс
Класс — это шаблон объекта.
Вернемся к нашему попугаю. Если мы схематично нарисуем его на бумаге, такой набросок будет являться классом. По нему можно сделать, например, чучело попугая.
Давайте создадим класс, который описывает попугая:
class Parrot:
pass
Для объявления класса Parrot
мы использовали ключевое слово class
. Из классов мы получаем экземпляры, созданные по подобию этого класса.
Объект
Объект — это экземпляр класса. Объявленный класс — это лишь описание объекта: ему не выделяется память.
Например, экземпляра класса Parrot
будет выглядеть так:
# obj — экземпляр класса Parrot
obj = Parrot()
Теперь разберемся, как написать класс и его объекты.
# Создаем класс и его объекты
class Parrot:
# атрибуты класса
species = "птица"
# атрибуты экземпляра
def __init__(self, name, age):
self.name = name
self.age = age
# создаем экземпляра класса
kesha = Parrot("Кеша", 10)
cookie = Parrot("Куки", 15)
# получаем доступ к атрибутам класса
print("Кеша — {}".format(kesha.__class__.species))
print("Куки тоже {}".format(cookie.__class__.species))
# получаем доступ к атрибутам экземпляра
print("{} — {}-летний попугай".format(kesha.name, kesha.age))
print("{} — {} летний попугай".format(cookie.name, cookie.age))
Вывод:
Кеша — птица
Куки тоже птица
Кеша — 10-летний попугай Куки — 15-летний попугай
Мы создали класс Parrot
. После этого мы объявили атрибуты — характеристики объекта.
Атрибуты объявлены внутри класса — в методе __init__
. Это метод-инициализатор, который запускается сразу же после создания объекта.
После этого мы создаем экземпляры класса Parrot
. kesha
и cookie
— ссылки на (значения) наши новые объекты.
Получить доступ к атрибуту класса можно так — __class__.species
. Атрибуты класса для всех экземпляров класса одинаковы. Точно так же мы можем получить доступ к атрибутам экземпляра — kesha.name
и kesha.age
. Но вот атрибуты каждого экземпляра класса уникальны.
Подробнее о классах и объектах.
Методы
Методы — функции, объявленные внутри тела класса. Они определяют поведения объекта.
# Создаем метод
class Parrot:
# атрибуты экземпляра
def __init__(self, name, age):
self.name = name
self.age = age
# метод экземпляра
def sing(self, song):
return "{} поет {}".format(self.name, song)
def dance(self):
return "{} танцует".format(self.name)
# создаем экземпляр класса
kesha = Parrot("Кеша", 10)
# вызываем методы экземпляра
print(kesha.sing("песенки"))
print(kesha.dance())
Вывод:
Кеша поет песенки
Кеша танцует
В этой программе мы объявили два метода: sing()
и dance()
. Они являются методами экземпляра, потому что они вызываются объектами — например, kesha
.
Наследование
Наследование — способ создания класса. Его суть заключается в том, что функциональность нового класса наследуются от уже существующего класса. Новый класс называется производным (дочерним). Существующий — базовым (родительским).
# родительский класс
class Bird:
def __init__(self):
print("Птица готова")
def whoisThis(self):
print("Птица")
def swim(self):
print("Плывет быстрее")
# дочерний класс
class Penguin(Bird):
def __init__(self):
# вызов функции super()
super().__init__()
print("Пингвин готов")
def whoisThis(self):
print("Пингвин")
def run(self):
print("Бежит быстрее")
peggy = Penguin()
peggy.whoisThis()
peggy.swim()
peggy.run()
Вывод:
Птица готова
Пингвин готов
Пингвин
Плывет быстрее
Бежит быстрее
В этой программе мы создаем два класса — Bird
(родительский) и Penguin
(дочерний). Дочерний класс наследует функции родительского. Это вы можете заметить по методу swim()
.
Но и дочерний класс изменяет функциональность родительского. Это можно заметить по методу whoisThis()
. Более того, мы расширяем функциональность родительского класса — создаем метод run()
.
Также мы используем функцию super()
внутри метода __init__()
. Это позволяет запускать метод __init__()
родительского класса внутри дочернего.
Инкапсуляция
Мы можем ограничить доступ к методам и переменным, что предотвратит модификацию данных — это и есть инкапсуляция. Приватные атрибуты выделяются нижним подчеркиванием: одинарным _
или двойным __
.
# Используем инкапсуляцию данных
class Computer:
def __init__(self):
self.__maxprice = 900
def sell(self):
print("Цена продажи: {}".format(self.__maxprice))
def setMaxPrice(self, price):
self.__maxprice = price
c = Computer()
c.sell()
# изменение цены
c.__maxprice = 1000
c.sell()
# используем функцию изменения цены
c.setMaxPrice(1000)
c.sell()
Вывод:
Цена продажи: 900
Цена продажи: 900
Цена продажи: 1000
Мы объявили класс Computer
.
Затем мы использовали метод __init__()
для хранения максимальной цены компьютера. Затем мы попытались изменить цену — безуспешно: Python воспринимает __maxprice
как приватный атрибут.
Как видите, для изменения цены нам нужно использовать специальную функцию — setMaxPrice()
, которая принимает цену в качестве параметра.
Полиморфизм
Полиморфизм — особенность ООП, позволяющая использовать одну функцию для разных форм (типов данных).
Допустим, нам нужно закрасить фигуру. Их форма может быть любой: прямоугольник, квадрат, круг. Одним и тем же методом мы можем раскрасить любую фигуру. Такой принцип и называется полиморфизмом.
# Используем полиморфизм
class Parrot:
def fly(self):
print("Попугай умеет летать")
def swim(self):
print("Попугай не умеет плавать")
class Penguin:
def fly(self):
print("Пингвин не умеет летать")
def swim(self):
print("Пингвин умеет плавать")
# общий интерфейс
def flying_test(bird):
bird.fly()
# создаем экземпляров класса
kesha = Parrot()
peggy = Penguin()
# передача объектов в качестве аргумента
flying_test(kesha)
flying_test(peggy)
Вывод:
Попугай умеет летать
Пингвин не умеет летать
В этой программе мы объявили два класса: Parrot
и Penguin
. В каждом из них описан общий метод fly()
. Но функции у них разные.
Для использование полиморфизма мы создали общий интерфейс — функцию flying_test()
. В качестве аргумента она принимает любой объект, после чего происходит вызов его собственного метода fly()
.
Что нужно запомнить:
- Объектно-ориентированное программирование упрощает понимание программы, делает ее эффективнее.
- Все классы являются общими — код можно использовать повторно.
- Все данные находятся в сохранности и защищены благодаря абстракции.
- Полиморфизм предоставляет общий интерфейс разным объектам, что делает код эффективнее.