Введение в объектно-ориентированное программирование на питоне

Введение

В этом полугодии мы с вами займемся клиент-серверными приложения - программами, которые могут взаимодействовать с друг другом через интернет. В процессе мы разберемся с:

  • работой с файлами;
  • обработкой исключений;
  • классами и объектами;
  • созданием игр в pygame;
  • передачей данных по сети;
  • блокирующими и неблокирующими сокетами;
  • веб-сервисами;
  • базами данных;
  • краулингом веб-страниц.

Результатом нашей работы станет собственная онлайн игра, социальная сеть и поисковая система, которые каждый из вас сможет написать.

Но прежде чем мы приступим к нашему первому проекту - онлайн игре, нам надо разобраться с обработкой исключений, классами и объектами, без которых работу с PyGame представить сложно. Именно этим мы с вами займемся в прологе.

Вспоминаем типы данных

Что такое объектно-ориентированное программирование?

Парадигма программирования - это подход, "философия", основные принципы, по которым строится программа. Существуют разные парадигмы (а значит подходы) программирования, самые известные из них - процедурная, объектно-ориентированная и функциональная. До этого момента, хотя мы и не говорили об этом, мы находились в процедурной парадигме.

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

Парадигмы находят свое отражение в языке программирования. Например, Си - это процедурный язык, а Haskell - функциональный. Большинство современных языков мультипарадигмальны - они позволяют писать код в разных стилях. В частности, Python - мультипарадигмальный - в нем можно писать как в процедурной логике, так и в объектно-оринетированной, а можно использовать и элементы функционального подхода.

В чем же отличие объектно-ориентированной парадигмы от процедурной? Чтобы осознать это, нам понадобится понятие "абстракция".

Абстракция - это модель объекта или явления, которая учитывает лишь необходимые для конкретной задачи свойства реального объекта или процесса и игнорует несущественные. Например, в детской игре в рыцарей, палка - это абстракция рыцарского меча.

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

Например, с точки зрения процедурного подхода игра Agar.io - это множество переменных с размерами, координатами и алгоритмом их изменения со временем, а с точки зрения объектно-ориентированного - множество взаимодействующих между собой объектов типа "шарик".

Понятие класса и объекта

Самое первое понятие, с которым мы встречаемся, начиная изучать ООП - это класс. Класс - это "чертеж" объекта реального мира, описание той самой абстракции. Но такие определения на первый взгляд кажутся весьма неочевидными, хотя и красивыми. Давайте сначала рассмотрим их с прикладной точки зрения.

Первое и ключевое отличие ООП языков от процедурных - это возможность описывать в них собственные типы данных. Если в процедурных языках есть фиксированное множество типов (числа, буквы, десятичные дроби и тп), то в объектно-ориентированных языках программист может придумывать и описывать собственные типы данных (слоны, дома, шарики, банковские счета и тп).

Класс - описание пользовательского типа данных. Класс включает в себя атрибуты (произвольные переменные) и методы - функции для работы со атрибутами.

Если у пользовательского типа данных есть такое красивое название, то почему бы не придумать такое же красивое для переменной это типа? Правильно, такие переменные и назваются объектами.

Объект - реализация класса, переменная.

Например, класс Собака - это описание собаки в общем виде, он говорит, что у собаки должны быть возраст, порода и умение гавкать. А объект этого класса - переменная Шарик - это конкретная дворняжка 2ух летнего возраста.

class Dog:
    def __init__(self):
        self.age = None

    def bark(self):
        print("Wow!")

if __name__ == "__main__":
    # это пес шарик
    sharik = Dog()
    # ему два года
    sharik.age = 2
    # Шарик, голос!
    sharik.bark()

Обратите внимание:

  1. В питоне атрибуты класса задаются в специальной функции, конструкторе, __init__. Кроме задания атрибутыов, в ней еще можно произвести начальную "настройку" объекта, но про это чуть позже.
  2. Методы задаются просто как функции, но получают обязательный параметр self (указатель на себя), который при вызове метода мы не передаем - он передается автоматически.

Почему класс Собака - это абстракция? На самом деле у собаки бесчисленное множество атрибутов, начиная от цвета шерсти (а он еще и меняется) и заканчивая, например, длиной пищевода. Но в данной модели мы их не учитываем, потому что они не важны для нашей задачи.

Кстати, вам не кажется, что вас обманули? Мы говорим о том, что мы описываем новые типы данных, а на самом деле просто объединяем много переменных в одну большую и добавляем к ним функции. Но с другой стороны, ведь все в природе состоит из одинаковых атомов...

Конструктор и деструктор

Существует два особенных метода в любом классе: конструктор и деструктор. Их особенность заключается в том, что они срабатывают автоматически.

Конструктор - это метод класса, автоматически вызываемый при создании объекта класса.

Деструктор - это метод класса, автоматически вызываемый при уничтожении объекта класса.

import time

class Alien:
    def __init__(self, name):
        self.name = name
        print(f'{self.name} is born!')

    def __del__(self):
        print(f'{self.name} is dead!')

def F():
    print("\nCreating Luna:")
    # создаем объект в локальной переменной - он будет жить до конца функции
    d = Alien("Luna")

    print("Function is running...")
    time.sleep(2)

    print("Function is over")

if __name__ == '__main__':
    # создаем объект, но не присваиваем его переменной - сборщик мусора сразу же его удалит
    print("Creating Luntik:")
    Alien("Luntik")

    # создаем объект вне функции - он будет жить до конца программы
    print("\nCreating Vupsen and Pupsen:")
    b = Alien("Vupsen")
    c = Alien("Pupsen")

    # но если нужно - можно удалить обхект и вручную
    print("\nManually deleting Pupsen:")
    del c

    # запускаем функцию
    F()

    print("\nReturn to main!")

Результат:

Creating Luntik:
Luntik is born!
Luntik is dead!

Creating Vupsen and Pupsen:
Vupsen is born!
Pupsen is born!

Manually deleting Pupsen:
Pupsen is dead!

Creating Luna:
Luna is born!
Function is running...
Function is over
Luna is dead!

Return to main!
Vupsen is dead!

UML

Если графическая запись процедурной программы - это блок-схема, то, наверное, есть графическое представление для ООП программ? Да, совершенно верно, и это - UML диаграмма. Прочитать о ней подробно можно здесь - UML запись классов.