Языки программирования

Объектно-ориентированное программирование в Python

Сегодня я расскажу вам о объектно-ориентированных возможностях в Python. Ведь с самого начала Python проектировался как объектно-ориентированный язык программирования.

Объектно-ориентированное программирование (ООП) — парадигма программирования, в которой основными концепциями являются понятия объектов и классов.

Класс — тип, описывающий устройство объектов. Объект — это экземпляр класса. Класс можно сравнить с чертежом, по которому создаются объекты.

Python соответствует принципам объектно-ориентированного программирования. В Python всё является объектами — и строки, и списки, и словари, и всё остальное.

Использование классов дает прежде всего преимущества абстрактного подхода в программировании.

1. Полиморфизм: в разных объектах одна и та же операция может выполнять различные функции. Слово «полиморфизм» имеет греческую природу и означает «имеющий многие формы».

Простым примером полиморфизма может служить функция count(), выполняющая одинаковое действие для различных типов обьектов: ‘abc’.count(‘a’) и [1, 2, ‘a’].count(‘a’). Оператор плюс полиморфичен при сложении чисел и при сложении строк.

2. Инкапсуляция: можно скрыть ненужные внутренние подробности работы объекта от окружающего мира. Это второй основной принцип абстракции. Он основан на использовании атрибутов внутри класса.

Атрибуты могут иметь различные состояния в промежутках между вызовами методов класса, вследствие чего сам объект данного класса также получает различные состояния — state.

3. Наследование: можно создавать специализированные классы на основе базовых. Это позволяет избегать написания повторного кода.

4. Композиция: объект может быть составным и включать в себя другие объекты.

Объектно-ориентированный подход в программировании подразумевает следующий алгоритм действий.

  1. Описывается проблема с помощью обычного языка с использованием понятий, действий, прилагательных.
  2. На основе понятий формулируются классы.
  3. На основе действий проектируются методы.
  4. Реализуются методы и атрибуты.

Теперь приступим к написанию своих классов и объектов на Python. Попробуем определить собственный класс и объект:
# Пример создания класса и объекта:
class Cat(object):
  """ Виртуальная кошка """
  def talk(self):
    print("Мяу")

Bagira = Cat()
Bagira.talk()
input()

# выведет: Мяу
Первым делом мы объявили класс Cat. Для создания класса используется служебное слово «class». Имя класса начинается с большой буквы. Наш класс будет создан с опорой на фундаментальный встроенный тип object, проще говоря, мы наследуем наш класс от object.

Строка «»» Виртуальная кошка «»» документирует класс.

Далее мы объявляем метод:
def talk(self):
    print("Мяу")
Метод talk() выводит строку «Мяу». У метода talk() есть параметр self, который мы не используем.

Далее мы создаем объект Bagira класса Cat() и вызываем его метод talk():
Bagira = Cat()
Bagira.talk()
Сначала создается переменная Bagira, затем используется точечная нотация для вызова метода talk(). При вызове метода talk() мы видим строку «Мяу».

Конструкторы

Конструкторы используются для инициализации объекта, например, для загрузки информации из базы данных. Пример:
class Cat(object):
  """ Виртуальная кошка """
  def __init__(self):
    print("Родилась новая кошка!")
  def talk(self):
    print("Мяу")
cat1 = Cat()
cat2 = Cat()
cat1.talk()
cat2.talk()
input()
Конструктор также называется методом инициализации, поэтому имя у него специфическое — __init()__. Имя конструктора нельзя изменять.
В примере мы создаем несколько объектов класса Cat. При создании объектов будет выведено сообщение о рождении новой кошки. Затем вызываем метод talk() каждого объекта.

Атрибуты класса

Атрибуты класса бывают двух видов:
  1. атрибуты данных;
  2. атрибуты-методы.
Атрибуты класса — это имена переменных вне функций и имена функций. Эти атрибуты наследуются всеми объектами, созданными на основе данного класса.
Атрибуты обеспечивают свойства и поведение объекта. Объекты могут иметь атрибуты, которые создаются в теле метода, если данный метод будет вызван для конкретного объекта. Использование атрибута:
class Cat(object):
  """ Виртуальная кошка """
  def __init__(self, name):
    print("Родилась новая кошка!")
    self.name = name
  def talk(self):
    print(self.name, ": Мяу")
    
cat1 = Cat("Багира")
cat2 = Cat("Василий")
cat1.talk
cat2.talk

input()
В данном примере мы добавили параметр name нашему конструктору. Теперь при создании объекта мы можем назвать наших кошек.

Далее мы выводим (в конструкторе) строку о рождении кошки, а вот следующая за print() строка как раз создает атрибут name и присваивает ему значение параметра name.

Также мы изменили метод talk(). Теперь он выводит имя кошки, то есть значение атрибута self.name. К атрибуту можно обратиться напрямую:
print(cat1.name)

# выведет: Багира
Аргумент self — это ссылка на создаваемый в памяти компьютера объект. Параметр self автоматически становится ссылкой на объект, по отношению к которому вызван метод.
Поэтому через метод self метод получает доступ к вызывающему объекту, к его атрибутам и методам.

Закрытые атрибуты и методы

По умолчанию все атрибуты и методы класса являются открытыми или публичными (public). Публичные методы можно вызывать за пределами класса, а к публичным атрибутам можно обращаться за пределами класса.
Существуют также и закрытые или приватные (private) методы и атрибуты. Приватный метод можно вызывать только внутри класса — в других методах.
К приватным атрибутам невозможно обратиться напрямую. В Python два знака подчеркивания означают, что атрибут будет закрытым. Рассмотрим пример:
def __init__(self, name):
   print("Родилась новая кошка!")
   self.name = name
   Cat.total += 1
   self.__w = 1
Атрибут __w является закрытым. Приватный метод объявляется аналогично:
def __private_method(self):
   print("Закрытый метод")
Если атрибут предназначен для служебного использования и его неправильное изменение может привести к сбою, то лучше его сделать закрытым и изменять только внутри класса — так вы сможете контролировать процесс изменения значения.

Управление доступом к закрытому атрибуту

Для управления доступом к закрытому атрибуту используются свойства — объект с методами, которые позволяют обращаться к атрибутам и ограничивают косвенный доступ.
Для создания свойства нужно написать метод, который возвращает значение. Перед таким методом нужно объявить декоратор @property.
Создав свойство, мы приоткрываем закрытый атрибут и делаем его доступным для чтения. Но с помощью свойств можно сделать его доступным не только для чтения, но и для записи.
Видео по уроку:

Программа «Виртуальная кошка»

Для закрепления теоретических знаний в области ООП создадим небольшую программу «Виртуальная кошка». В данной программе можно будет покормить виртуальную кошку, а также поиграть и взвесить. Листинг программы:
class Cat(object):
    """ Виртуальная кошка """
    total = 0
    @staticmethod
    def count():
        print("Всего кошек: ", Cat.total)
    
    def __init__(self):
        print("Родилась новая кошка!")
        self.name = input("Как мы её назовём? ")
        Cat.total += 1
        self.__w = 300
        self.hunger = 1
        
    def __str__(self):
        res = "Объект класса Cat\n name: " + self.name + "\nBec: " + str(self.__w)
        return res
        
    @property
    def weight(self):
        return self.__w
    
    def talk(self):
        print(self.name, ": Мяу")
    
    def eat(self):
        if self.hunger == 5:
            print("Кошка не голодная")
        else:
            self.hunger += 1
            self.__w += 30
            print("Мур!")

    def play(self):
        self.talk()
        self.__w -= 5
        if self.hunger > 0:
            self.hunger -= 1
        else:
            self.hunger = 1
    
def main():

    bagira = Cat()
    choice = None
    while choice != "0":
        print \
        ("""
        Что будем делать?
            
        0 - Выйти
        1 - Поговорить с кошкой
        2 - Покормить
        3 - Поиграть
        4 - Взвесить
        """)
            
        choice = input(">>: ")
        print()

        # exit
        if choice == "0":
            print("Пока.")
            
        # послушать
        elif choice == "1":
            bagira.talk()
                
        # покормить
        elif choice == "2":
            bagira.eat()
                
        # поиграть
        elif choice == "3":
            bagira.play()
        elif choice == "4":
            print("Вес: ", bagira.weight, " гр. ")
            
        # неправильный ввод
        else:
            print("\nНеправильный ввод!")  
main()            
input()
Декоратор @staticmethod — это просто функция внутри класса. Вы можете вызывать их обоих как с инициализацией класса так и без создания экземпляра класса. Обычно это применяется в тех случаях, когда у вас есть функция, которая, по вашему убеждению, имеет связь с классом.

При рождении кошки пользователю дается возможность выбрать имя кошки. Также устанавливается начальный вес в 300 грамм и уровень голода 1, что означает, что кошка голодна.

При каждом кормлении уровень голода повышается. Когда он достигает 5, кошка считается не голодной. При каждой игре с питомцем уровень голода понижается на единицу, то есть после того, как с кошкой поиграли, ее можно покормить.

При каждом кормлении вес кошки увеличивается на 30 граммов. При каждой игре с кошкой она теряет 5 грамм веса.

Вывод программы:
Видео по созданию программы:
Самоучитель по Python