Программирование на языке Python
Наследование и полиморфизм
План лекции
- Понятие наследования
- Метод super()
- Примеры применения механизма наследования в действии
- Полиморфизм.
- Примеры реализации полиморфизма
Объектно-ориентированное программирование
- Объектно-ориентированное программирование (ООП) — методология программирования, основанная на представлении программы в виде совокупности объектов, каждый из которых является экземпляром определённого класса, а классы образуют иерархию наследования.
- В основе ООП лежит простая и элегантная идея, в соответствии с которой главное в программе - это данные . Именно они определяют, какие методы будут использоваться для их обработки.
- Т. е. данные первичны, код для обработки этих данных - вторичен.
Свойства объектно-ориентированного программирования
- Наследование (возможность создания новых классов на базе старых);
- Инкапсуляция (возможность изолирования данных и методов от остальной части программы);
- Полиморфизм (возможность выполнять одну и ту же операцию с классами различных типов, если они имеют общие свойства)..
Наследование
Механизм наследования позволяет создать новый класс на основе уже существующего.
При этом новый класс включает в себя как свойства и методы родительского класса, так и новые (собственные) атрибуты.
Эти новые атрибуты и отличают свежесозданный класс от его родителя.
Наследование
Для того, чтобы в Python создать новый класс с помощью механизма наследования, необходимо воспользоваться следующим синтаксисом:
Синтаксис
class ():
Теперь давайте рассмотрим пример применения механизма наследования в действии.
Перед нами класс Phone (Телефон), у которого есть одно свойство is_on и три метода:
- Инициализатор: _init_()
- Включение: turn_on()
- Звонок: call()
Наследование
Python
# Родительский класс
class Phone :
# Инициализатор
def __init__( self):
self.is_on = False
# Включаем телефон
def turn_on (self):
self.is_on = True
# Если телефон включен, делаем звонок
def call (self):
if self.is_on:
print(‘ Making call... ')
my_phone = Phone() dir(my_phone) ['_class_', '_delattr_', '_dict_', '_dir_', '_doc_', '_eq_', '_format_', '_ge_', '_getattribute_', '_gt_', '_hash_', '_init_', '_init_subclass_', '_le_', '_lt_', '_module_', '_ne_', '_new_', '_reduce_', '_reduce_ex_', '_repr_', '_setattr_', '_sizeof_', '_str_', '_subclasshook_', '_weakref_', 'call', 'is_on', 'turn_on'] Среди данной совокупности атрибутов нас больше всего интересуют пользовательские свойства и методы: '_ init_', 'call', 'is_on', 'turn_on‘ ." width="640"
Наследование
В результате объект такого класса получит следующий набор атрибутов:
Python
my_phone = Phone()
dir(my_phone)
['_class_', '_delattr_', '_dict_', '_dir_', '_doc_', '_eq_', '_format_', '_ge_', '_getattribute_', '_gt_', '_hash_', '_init_', '_init_subclass_', '_le_', '_lt_', '_module_', '_ne_', '_new_', '_reduce_', '_reduce_ex_', '_repr_', '_setattr_', '_sizeof_', '_str_', '_subclasshook_', '_weakref_', 'call', 'is_on', 'turn_on']
Среди данной совокупности атрибутов нас больше всего интересуют пользовательские свойства и методы: '_ init_', 'call', 'is_on', 'turn_on‘ .
Наследование
А теперь предположим, что мы захотели создать новый класс - MobilePhone (Мобильный телефон).
Хоть этот класс и новый, но это по-прежнему телефон, а значит - его все так же можно включить и по нему можно позвонить.
А раз так, то нам нет смысла реализовывать этот функционал заново, а можно просто унаследовать его от класса Phone .
Выглядит это так:
Наследование
Python
class Phone:
def __init__( self):
self.is_on = False
def turn_on (self):
self.is_on = True
def call (self):
if self.is_on:
print(‘ Making call... ')
Наследование
# Унаследованный класс
class MobilePhone (Phone):
# Добавляем новое свойство battery
def __init__ (self):
super().__init__()
self.battery = 0
# Заряжаем телефон на величину переданного значения
def charge (self, num):
self.battery = num
print(f ' Charging battery up to ... {self.battery}% ')
my_mobile_phone = MobilePhone() dir(my_mobile_phone) ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'battery', 'call', 'charge', 'is_on', 'turn_on']" width="640"
Наследование
Как вы видите, в новом классе добавились свойство battery и метод charge() . При этом мы помним, что это класс-потомок Phone , а значит от унаследовал и его функционал тоже. Создадим объект нового класса и посмотрим список его атрибутов:
Python
my_mobile_phone = MobilePhone()
dir(my_mobile_phone)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'battery', 'call', 'charge', 'is_on', 'turn_on']
from phone import Phone, MobilePhone # Создаем объект класса MobilePhone my_mobile_phone = MobilePhone()" width="640"
Наследование
Теперь мы видим, что пользовательские атрибуты состоят из унаследованных ( 'is_on', 'call', 'turn_on ') и новых ('_init_', 'battery', 'charge ').
Все они теперь принадлежат классу MobilePhone .
Пример использования:
Python
# Импортируем оба класса
from phone import Phone, MobilePhone
# Создаем объект класса MobilePhone
my_mobile_phone = MobilePhone()
my_mobile_phone.turn_on() my_mobile_phone.call() Making call... # Заряжаем мобильный телефон my_mobile_phone.charge(76) Charging battery up to ... 76%" width="640"
Наследование
# Включаем телефон и делаем звонок
my_mobile_phone.turn_on()
my_mobile_phone.call()
Making call...
# Заряжаем мобильный телефон
my_mobile_phone.charge(76)
Charging battery up to ... 76%
Наследование
Что такое super ?
Как вы могли заметить, в инициализаторе (метод _init_ ) наследуемого класса вызывается метод super() . Что это за метод и зачем он нужен?
Главная задача этого метода - дать возможность наследнику обратиться к родительскому классу .
В классе родителе Phone есть свой инициализатор, и когда в потомке MobilePhone мы так же создаем инициализатор (а он нам действительно нужен, так как внутри него мы хотим объявить новое свойство) - мы его перегружаем .
Иными словами, мы заменяем родительский метод _init_() собственным одноименным методом.
Наследование
Это чревато тем, что родительский метод просто в принципе не будет вызван, и мы потеряем его функционал в классе наследнике.
В конкретном случае, потеряем свойство is_on .
Чтобы такой потери не произошло, мы можем:
1. Внутри инициализатора класса-наследника вызвать инициализатор родителя (для этого вызываем метод super()._init_() )
2. А затем просто добавить новый функционал.
Наследование
Давайте еще раз взглянем на метод _init_() класса MobilePhone :
def __init__(self):
____super().__init__()
____self.battery = 0
Обратите внимание, что вызывать родительский метод необходимо в первую очередь.
Полиморфизм
- Полиморфизм позволяет перегружать одноименные методы родительского класса в классах-потомках.
- Что дает возможность использовать перегруженный метод в случаях, когда мы еще не знаем, для какого именно класса он будет вызван.
- Мы просто указываем имя метода, а объект класса, к которому он будет применен, определится по ходу выполнения программы.
Базовый класс
Метод Х
Производный класс
Метод Х
Полиморфизм
[греч. poly — много и morphe — вид, форма, образ]
Полиморфизм ( polymorphism)
- имеется несколько реализаций алгоритма выбор реализации осуществляется в зависимости от типа объекта и типа параметров
- имеется несколько реализаций алгоритма
- выбор реализации осуществляется в зависимости от типа объекта и типа параметров
Механизмы реализации:
- Перегрузка ( overload) метода Переопределение ( override) метода
- Перегрузка ( overload) метода
- Переопределение ( override) метода
(Химия) В химии свойство тел, одинаковых по химическому сос-таву и построению, принимать различные кристаллические формы (Графит и Алмаз)
(Биология) Существование особей одного и того же вида с раз-личными по строению формами. Полиморфизм пчел и муравьев.
Полиморфизм.
Пример переопределения методов
Прямоугольник
Площадь
Фигура
Площадь
Круг
Площадь
Связывание объекта и метода
Статическое (раннее)
Динамическое (позднее)
20
Полиморфизм
Чтобы стало более понятно, давайте рассмотрим пример:
# Родительский класс
class Phone :
def __init__( self):
self.is_on = False
def turn_on (self):
pass
def call (self):
pass
# Метод, который выводит короткую сводку по классу Phone
def info (self):
print(f 'Class name: {Phone.__name__}')
print(f 'If phone is ON: {self.is_on}')
Полиморфизм
# Унаследованный класс
class MobilePhone (Phone):
def __init__( self):
super().__init__()
self.battery = 0
# Такой же метод, который выводит короткую сводку по классу MobilePhone
# Обратите внимание, что названия у методов совпадают - оба метода называются info()
# Однако их содержимое различается
def info (self):
print(f 'Class name: {MobilePhone.__name__}')
print(f 'If mobile phone is ON: {self.is_on}')
print(f 'Battery level: {self.battery}')
Полиморфизм
# Демонстрационная функция
# Создаем список из классов
# В цикле перебираем список и для каждого элемента списка (а элемент - это класс)
# создаем объект и вызываем метод info()
# Главная особенность: запись object.info() не дает инфор - мацию об объекте, для которого будет вызван метод info()
# Это может быть объект класса Phone, а может - объект класса MobilePhone
# И только в момент исполнения кода становится ясно, для какого именно объекта нужно вызывать метод info()
def show_polymorphism ():
for item in [Phone, MobilePhone]:
print('-------')
object = item()
object.info()
from phone import Phone, MobilePhone from phone import show_polymorphism show_polymorphism() ------- Class name: Phone If phone is ON: False ------- Class name: MobilePhone If mobile phone is ON: False Battery level: 0" width="640"
Полиморфизм
Вызываем наш демонстрационный метод:
Python
from phone import Phone, MobilePhone
from phone import show_polymorphism
show_polymorphism()
-------
Class name: Phone
If phone is ON: False
-------
Class name: MobilePhone
If mobile phone is ON: False
Battery level: 0
Итак:
- Мы с вами узнали, почему при разработке современных программ использование объектно-ориентированного подхода является обязательным условием.
- Также разобрались в понятиях Класс, Объект (Экземпляр), Атрибут, Свойство (Поле), Метод.
- Далее посмотрели, какими эти самые атрибуты, свойства и методы бывают.
- А еще научились отличать Protected атрибуты от Private и разобрались, как реализована модель уровней доступа к атрибутам непосредственно в Python.
- Научились реализовывать наследование и полиморфизм
- Теперь давайте постараемся эти знания применить на практике.
Задача "Покупка дома"
С помощью подхода ООП и средств Python в рамках данной задачи необходимо реализовать следующую предметную структуру:
Классовая структура
- Есть Человек , характеристиками которого являются: 1. Имя 2. Возраст 3. Наличие денег 4. Наличие собственного жилья
- Человек может: 1. Предоставить информацию о себе 2. Заработать деньги 3. Купить дом
Задача "Покупка дома"
- Также же есть Дом , к свойствам которого относятся: 1. Площадь 2. Стоимость
- Для Дома можно: 1. Применить скидку на покупку
- Также есть Небольшой Типовой Дом , обязательной площадью 40м 2 .
Задание. Часть 1. Класс Human
1. Создайте класс Human . 2. Определите для него два статических поля: default_name и default_age . 3. Создайте метод __init__() , который помимо self принимает еще два параметра: name и age . Для этих параметров задайте значения по умолчанию, используя свойства default_name и default_age . В методе __init__() определите четыре свойства: Публичные - name и age . Приватные - money и house . 4. Реализуйте справочный метод info() , который будет выводить поля name , age , house и money .
5. Реализуйте справочный статический метод default_info() , который будет выводить статические поля default_name и default_age .
Задание. Часть 1. Класс Human
6. Реализуйте приватный метод make_deal() , который будет отвечать за техническую реализацию покупки дома: уменьшать количество денег на счету и присваивать ссылку на только что купленный дом. В качестве аргументов данный метод принимает объект дома и его цену. 7. Реализуйте метод earn_money() , увеличивающий значение свойства money . 8. Реализуйте метод buy_house() , который будет проверять, что у человека достаточно денег для покупки, и совершать сделку. Если денег слишком мало - нужно вывести предупреждение в консоль. Параметры метода: ссылка на дом и размер скидки
Задание. Часть 2. Класс House
1. Создайте класс House 2. Создайте метод __init__() и определите внутри него два динамических свойства: _area и _price . Свои начальные значения они получают из параметров метода __init__() 3. Создайте метод final_price() , который принимает в качестве параметра размер скидки и возвращает цену с учетом данной скидки.
Задание. Часть 3. Класс SmallHouse
1. Создайте класс SmallHouse , унаследовав его функционал от класса House 2. Внутри класса SmallHouse переопределите метод __init__() так, чтобы он создавал объект с площадью 40м 2
.
Задание. Часть 4. Выполнение
1. Вызовите справочный метод default_info() для класса Human 2. Создайте объект класса Human 3. Выведите справочную информацию о созданном объекте (вызовите метод info() ). 4. Создайте объект класса SmallHouse 5. Попробуйте купить созданный дом, убедитесь в получении предупреждения. 6. Поправьте финансовое положение объекта - вызовите метод earn_money() 7. Снова попробуйте купить дом 8. Посмотрите, как изменилось состояние объекта класса Human


Наследование и полиморфизм 