Классы. Наследование

Цель: научиться применять на практике наследование классов.

Примеры

Пример (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