Введение в объектно-ориентированное программирование на питоне
Введение
В этом полугодии мы с вами займемся клиент-серверными приложения - программами, которые могут взаимодействовать с друг другом через интернет. В процессе мы разберемся с:
- работой с файлами;
- обработкой исключений;
- классами и объектами;
- созданием игр в 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()
Обратите внимание:
- В питоне атрибуты класса задаются в специальной функции, конструкторе,
__init__
. Кроме задания атрибутыов, в ней еще можно произвести начальную "настройку" объекта, но про это чуть позже. - Методы задаются просто как функции, но получают обязательный параметр
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 запись классов.