Экспресс-курс по Python: Лекция №4 Объектно-ориентированное программирование

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

Объектно-ориентированное программирование состоит из трех китов:

  • инкапсуляция
  • наследование
  • полиморфизм

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

Создадим класс в Python. Для этого необходимо определить класс (новый тип данных) и создать объект, называемый экземпляром класса. Мы рекомендуем имена классов начинать с заглавной буквы "T", подчеркивая тем самым, что речь идет о типе данных. Делается это так:

  1. class TAnimal:
  2. name = ""
  3. def __init__(self, name):
  4. self.name = name
  5. def say(self):
  6. print(self.name)

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

  1. Animal = TAnimal("Обезьяна")
  2. Animal.say()

Обезьяна

Рассмотрим синтаксис Python при создании классов. Все начинается с ключевого слова class. далее в блоке из отступов мы определяем переменные, которые будем называть полями и функции, которые называются методами. Методы определяются, как обычные функции и могут возвращать значения. Единственное отличие состоит в том, что у всех методов есть обязательный первый параметр, который по традиции всегда называем self в котором передается ссылка на экземпляр класса. Поэтому когда внутри класса метод хочет обратиться к своему полю, то необходимо использовать конструкцию self.name. Заметим, что при вызове методов мы первый параметр не задаем.

Далее, у каждого класса есть метод, с именем __init__, который называется конструктором класса. Этот метод вызывается в момент создания экземпляра Animal = TAnimal("Обезьяна"). Конструктор может иметь любое количество параметров.

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

  1. class TAnimal:
  2. name = ""
  3. def __init__(self, name):
  4. self.name = name
  5. def say(self):
  6. print(self.name)
  7.  
  8. class TCat(TAnimal):
  9. def may(self):
  10. print("Мяу!")
  11.  
  12. Cat = TCat("Кошка")
  13. Cat.say()
  14. Cat.may()

Кошка
Мяу!

Мы видим, что у наследованного класса сохранился конструктор и метод say. Заметим, что в Python также как и в C++ есть возможность множественного наследования, когда класс наследует сразу несколько классов. Впрочем, как показывает практика, множественное наследование является излишним и часто приводит к проблемам, поэтому мы не будем рассматривать множественное наследование.

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

  1. class TCat(TAnimal):
  2. def __init__(self):
  3. super().__init__("Кошка")
  4. def may(self):
  5. print("Мяу!")
  6.  
  7. Cat = TCat()
  8. Cat.say()
  9. Cat.may()

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

Самое удивительное в полиморфизме, что изменяя метод, он меняется даже когда на него есть ссылки родительского класса. Рассмотрим еще один пример. Пусть у нас есть класс:

  1. class TDo:
  2. def Operation(self, x, y):
  3. return x + y
  4. def Run(self):
  5. x = int(input("Enter x > "))
  6. y = int(input("Enter y > "))
  7. z = self.Operation(x, y)
  8. print("Result = " + z.__str__())
  9.  
  10. Do = TDo()
  11. Do.Run()

Enter x > 3
Enter y > 5
Result = 8

С помощью полиморфизма заменим функцию Operation на другую в наследном классе:

  1. class TDo2(TDo):
  2. def Operation(self, x, y):
  3. return x * y

В результате выполнения кода

  1. Do = TDo2()
  2. Do.Run()

Получим:

Enter x > 3
Enter y > 5
Result = 15

Мы видим, что результат выполнения метода Run изменился, хотя мы не меняли код этого метода.

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

Home | Лекции | Python | Видео | Скачать | Ссылки
Copyright (c) 2017, Roman Shamin
55
6679
31