Классы. Наследование
Цель: научиться применять на практике наследование классов.
Примеры
Пример (ex01.py)
Опишите ряд классов, необходимых для реализации противников в компьютерной игре:
NPC
– базовый классSwordsman
– мечник (наследует класс NPC). Наносит случайный урон из заданного диапазона.Mage
– маг (наследует класс NPC). Наносит урон равный удвоенному количеству маны. Не может атаковать, если мана закончилась.
Диаграмма классов приведена на следующем рисунке:
Наберите и проверьте работу класса NPC
:
from random import randint
class NPC:
"""Мирный персонаж"""
def __init__(self, name, hp):
self.name = name
self.hp = hp
def __str__(self):
return f"Имя: {self.name}, Очки здоровья: {self.hp}"
def attack(self):
print("Не могу атаковать!")
return 0
# удалите следующие строки после тестирования класса
char = NPC("Торговец", 15)
print(char)
char.attack()
После того, как убедитесь в работоспособности класса, добавьте к текущей программе описание класса Swordsman
:
class Swordsman(NPC):
"""Мечник"""
def __init__(self, name, hp, min_damage, max_damage):
super().__init__(name, hp)
self.min_damage = min_damage
self.max_damage = max_damage
def attack(self):
damage = randint(self.min_damage, self.max_damage)
print(f"Мечник {self.name} нанёс {damage} урона!")
return damage
# удалите следующие строки после тестирования класса
dd = Swordsman("Мечник", 50, 10, 20)
dd.attack()
После того, как убедитесь в работоспособности класса, добавьте к текущей программе описание класса Mage
:
class Mage(NPC):
"""Маг"""
def __init__(self, name, hp, mana):
super().__init__(name, hp)
self.mana = mana
def attack(self):
damage = self.mana * 2
if self.mana <= 0:
print("Не могу атаковать! Мана закончилась.")
else:
self.mana = self.mana - 5
print(f"Маг {self.name} нанёс {damage} урона!")
return damage
# удалите следующие строки после тестирования класса
wiz = Mage("Колдун", 100, 5)
wiz.attack()
wiz.attack()
Создадим объекты каждого класса. После этого разместим их в списке attack_order
, чтобы сформировать очередь для атак. В цикле for
переберём элементы списка и вызовем метод attack()
каждого из объектов:
# создаём экземпляры классов
hobbit = NPC("Бильбо", 15)
aragorn = Swordsman("Арагорн", 50, 5, 10)
gandalf = Mage("Гендальф", 100, 5)
# описываем очерёдность хода
attack_order = [hobbit, gandalf, aragorn, gandalf]
# перебираем очередь атаки
for hero in attack_order:
# выводим информацию о персонаже
print(hero)
# атакуем
hero.attack()
Пример:
Имя: Бильбо, Очки здоровья: 15
Не могу атаковать!
Имя: Гендальф, Очки здоровья: 100
Маг Гендальф нанёс 10 урона!
Имя: Арагорн, Очки здоровья: 50
Мечник Арагорн нанёс 8 урона!
Имя: Гендальф, Очки здоровья: 100
Не могу атаковать! Мана закончилась.
Задания для самостоятельной работы
Задание № 1 (sam01.py)
Дан класс Animal
, который содержит поле nickname
(кличка), конструктор и метод __str__
.
Опишите классы Cat
(Кот) и Parrot
(Попугай), которые будут наследниками класса Animal
. Оба класса должны содержать методы voice()
, который выводит не экран фразу произнесённую животным. Класс Cat
должен содержать метод run()
(бежать), который выводит на экран фразу Побежали!. Класс Parrot
должен содержать метод fly()
(летать), который выводит на экран фразу Полетели!.
Диаграмма классов изображена на следующем рисунке:
Шаблон программы:
class Animal:
"""Родительский класс"""
def __init__(self, nickname):
"""Конструктор класса"""
self.nickname = nickname
def __str__(self):
"""Преобразование объекта в строку"""
return "Моя кличка " + self.nickname
# начало класса Cat, который наследует класс Animal:
# описание метода voice(self)
# описание метода run(self)
# начало класса Parrot, который наследует класс Animal:
# описание метода voice(self)
# описание метода fly(self)
# Создание объекта класса Cat
barsik = Cat("Барсик")
print(barsik) # выводим информацию об объекте (__str__)
barsik.voice() # произносим фразу
barsik.run() # кот побежал
# Создание объекта класса Parrot
kesha = Parrot("Кеша")
print(kesha) # выводим информацию об объекте (__str__)
kesha.voice() # произносим фразу
kesha.fly() # попугай полетел
Пример:
Моя кличка Барсик
Мяу!
Побежали!
Моя кличка Кеша
Чирик!
Полетели!
Задание № 2 (sam02.py)
Опишите класс Message
, который описывает абстрактное сообщение, которое можно отправить в мессенджере. Класс содержит следующие поля:
sender
- отправитель сообщения (строка)recipient
- получатель сообщения (строка)
Добавьте к описанию класса следующие методы:
__init__(sender, recipient)
- конструктор классаshowHeader()
- выводит заголовок сообщения с информацией об отправителе и получателе (см. пример)
Используйте данный шаблон класса:
class Message:
def __init__(self, sender, recipient):
self.sender = sender # отправитель
self.recipient = recipient # получатель
def showHeader(self):
# вывод на экран отправителя
# вывод на экран получателя
Затем опишите класс TextMessage
который должен наследовать класс Message
. Используем его для описания текстового сообщения.
Помимо полей родительского класса, класс TextMessage
содержит поля:
text
- содержит текст сообщения (строка)
Помимо методов родительского класса, класс TextMessage
содержит следующие методы:
__init__(sender, recipient, text)
- конструктор классаsend()
- выводит заголовок сообщения (методshowHeader()
) а затем текст сообщения.
Используйте данный шаблон класса:
class TextMessage(Message):
def __init__(self, sender, recipient, text):
super().__init__(sender, recipient)
self.text = text
def send(self):
# вызываем метод showHeader()
# выводим содержимое поля text
# удалите следующие строки после тестирования работы программы
mess = TextMessage("Саша", "Оля", "Привет, как дела?")
mess.send()
Затем опишите класс StickerMessage
который должен наследовать класс Message
. Используем его для описания сообщения-стикера.
Помимо полей родительского класса, класс StickerMessage
содержит поля:
sticker
- содержит отправляемое изображение (строка)count
- счётчик, который хранит количество раз, сколько сообщение с этим стикером было прочитано.
Так как мы пока не затрагивали работу с файлами, вместо реального изображения в поле sticker
будем хранить строку, изображающую стикер:
(̶◉͛‿◉̶)
ʕ•́ᴥ•̀ʔっ
(ง︡‘-’︠)ง
(͡• ͜ʖ ͡•)
(─‿‿─)
Помимо методов родительского класса, класс StickerMessage
содержит следующие методы:
__init__(sender, recipient, sticker)
- конструктор класса. Полюcount
в качестве начального значения присваивается 1.send()
- выводит заголовок сообщения (методshowHeader()
) а затем содержимое поляsticker
. В этой же строке выводится значение из поляcount
. Текущее значение поляcount
увеличивается на 1.
Используйте данный шаблон класса:
class StickerMessage(Message):
def __init__(self, sender, recipient, sticker):
# вызываем конструктор родительского класса (см. класс TextMessage)
# сохраняем поле sticker
# полю count присваиваем 1
def send(self):
# вызываем метод showHeader()
# вывод на экран поля sticker и count
# увеличиваем текущее значение поля count на 1
# удалите следующие строки после тестирования работы программы
sticker = StickerMessage("Саша", "Оля", "ʕ•́ᴥ•̀ʔっ")
sticker.send()
sticker.send()
Создадим несколько сообщений разных типов и поместим их в список:
m1 = TextMessage("Саша", "Иван", "Привет, завтра зачёт?")
s1 = StickerMessage("Иван", "Саша", "(͡• ͜ʖ ͡•)")
m2 = TextMessage("Саша", "Иван", "Понятно. Ты готов?")
s2 = StickerMessage("Саша", "Иван", "(̶◉͛‿◉̶)")
mes_list = [m1, s1, m2, s1, s2]
for mess in mes_list:
mess.send()
print()
Пример:
Отправитель: Саша
Получатель: Иван
Привет, завтра зачёт?
Отправитель: Иван
Получатель: Саша
(͡• ͜ʖ ͡•) 1
Отправитель: Саша
Получатель: Иван
Понятно. Ты готов?
Отправитель: Иван
Получатель: Саша
(͡• ͜ʖ ͡•) 2
Отправитель: Саша
Получатель: Иван
(̶◉͛‿◉̶) 1