Объектно-ориентированное программирование в 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()

Что нужно запомнить:

  • Объектно-ориентированное программирование упрощает понимание программы, делает ее эффективнее. 
  • Все классы являются общими — код можно использовать повторно. 
  • Все данные находятся в сохранности и защищены благодаря абстракции.
  • Полиморфизм предоставляет общий интерфейс разным объектам, что делает код эффективнее. 
codechick

СodeСhick.io - простой и эффективный способ изучения программирования.

2024 ©