Python · Синтаксис · Початковий рівень
ООП: успадкування та dunder-методи
Завдання по ООП в Python на спадкування та спеціальні методи: дочірні класи, перевизначення поведінки, __str__, __len__, розширення базових класів та робота з об'єктною моделлю.
Короткий вступ до теми та пояснення перед вправами (вправи нижче):
Dunder-методи — __str__ і __len__
#Патерни успадкування і dunder-методів
#Швидкий довідник: наслідування та dunder-методи
#Вправи:
Animal і Dog через наслідування.
#Створіть клас Animal , у створюваних ним об’єктів має бути атрибут name . Значення в атрибут передається під час ініціалізації об’єкта. Тобто — об’єкт описує тварину (будь-яку) і зберігає її кличку. Потім створіть клас Dog , який наслідується від Animal . У класі Dog додайте метод __str__ , який повертає рядок, у якому міститься тип тварини (ахахах, вибачаюсь, "тип тварини" звучить смішно) та її кличка: "Dog: Rex" # приклад рядка, що повертається Сенс такий: Базовий клас Animal зберігає основну "поведінку" об’єктів тварин, а клас Dog деталізує її під конкретний тип тварини.
class Animal:
pass
class Dog(Animal):
pass
Рішення
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def __str__(self):
return f"Dog: {self.name}"
# Можна деталізувати:
class Dog(Animal):
def __str__(self):
return f"Dog: {self.name}"
def tell(self):
print("Bark!!!") # "Гав" англійською. Собакам теж потрібен перекладач..
class Cat(Animal):
def __str__(self):
return f"Cat: {self.name}"
def tell(self):
print("Cats don't care about human commands!")
# self все одно потрібно прийняти, навіть якщо в методі не використовується.
Book із рядковим представленням.
#Створіть клас Book з атрибутами для об’єктів title і pages . Додайте метод __str__ , який повертає: "Book: Python Basics (120 pages)" Можна додати метод __len__ , який повертатиме кількість сторінок книги.
class Book:
pass
Рішення
class Book:
def __init__(self, title, pages):
self.title = title
self.pages = pages
def __len__(self):
return self.pages
def __str__(self):
return f"Book: {self.title} ({self.pages} pages)"
User і Admin.
#Давайте ще раз пройдемо щось схоже на перше: Умова: Створіть клас User з атрибутом name . Створіть клас Admin , який наслідується від User . Перевизначте метод __str__ , щоб вивід був: "Admin: Alex" А у звичайного User виводилось через __str__ просто: "Alex"
class User:
pass
class Admin(User):
pass
Рішення
class User:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
class Admin(User):
def __str__(self):
return f"Admin: {self.name}"
Коробка з предметами.
#Створіть клас Box з атрибутом items_count . Додайте метод __len__ , який повертає кількість предметів. І додайте методи add і remove , які можуть класти та забирати об’єкт із коробки. Це можна зробити просто через лічильник items_count (просто рахувати без складнощів), або зробити список предметів у коробці (рядків) і підлаштувати клас для роботи зі списком (список — атрибут об’єкта).
class Box:
pass
Рішення
class Box:
def __init__(self, items_count):
self.items_count = items_count
def add(self, count=1):
self.items_count += count
def remove(self, count=1):
self.items_count -= count
def __len__(self):
return self.items_count
# Зі списком складніше і по-іншому:
class Box:
def __init__(self):
self.items = []
self.items_count = len(self.items)
def add(self, item):
self.items.append(item)
self.items_count = len(self.items)
def remove(self, item):
if item in self.items:
self.items.remove(item)
self.items_count = len(self.items)
else:
print("Кіт із коробки каже: Такого тут немає, до побачення!")
def __len__(self):
return self.items_count
Message зі spam-прапором.
#Створіть клас Message з атрибутом text і атрибутом spam. У text текст повідомлення передається під час створення об’єкта, а атрибут spam спочатку просто має значення None. Додайте методи: - is_spam - який перевіряє, чи є в тексті якісь слова як критерій спаму, і повертає True/False - під час виклику методу is_spam , окрім повернення цим методом True/False, у атрибуті self.spam встановлюється відповідне значення. - і додайте __len__ , який повертає довжину тексту.
class Message:
pass
Рішення
class Message:
def __init__(self, text):
self.text = text
self.spam = None
self.spam_triggers = ["куплю", "дорого"] # Так, я про це не говорив,
# але й не забороняв.
def is_spam(self):
if self.spam is not None: # пояснення нижче, тут я вже "перегнув"
return self.spam
for trigger in self.spam_triggers:
if trigger in self.text:
self.spam = True
return True
self.spam = False
return False
def __len__(self):
return len(self.text)
# Наявність self.spam після першого виклику is_spam дозволить при повторних перевірках
# не запускати механізм перевірки, а просто подивитися в УЖЕ ОБЧИСЛЕНИЙ атрибут (оптимізація).
m = Message("Куплю старые телевизоры дорого!!!")
if m.is_spam(): # виконується реальна перевірка
...
if m.is_spam(): # вже буде взято із self.spam
...
Employee і Manager.
#Створіть клас Employee (працівник) з атрибутами name і salary . Зробіть метод у Employee , який може змінювати зарплату. Створіть клас Manager , який наслідується від Employee . Перевизначте метод __str__ так, щоб вивід був: Manager John earns 5000 Переконайтеся, що метод зміни зарплати також працює для Manager
class Employee:
pass
class Manager(Employee):
pass
Рішення
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
def change_salary(self, coef): # наприклад коефіцієнт 1.1 - збільшити на 10%
self.salary = round(self.salary * coef) # без другого аргументу округлить до цілого
class Manager(Employee):
def __str__(self):
return f"Manager {self.name} earns {self.salary}"
chel = Manager("Bob", 1000)
chel.change_salary(1.1)
print(chel) # Manager Bob earns 1100
Playlist і CustomPlaylist.
#Створіть клас Playlist з атрибутом songs (список). Додайте методи add , remove - додавання/видалення пісень. Додайте метод reverse , який перевертає список треків. Додайте метод __len__ , який повертає кількість пісень. Додайте метод __bool__ - порожній плейлист чи ні. А потім зробіть клас-нащадок CustomPlaylist , у якому метод add дозволятиме додавання дублікатів пісень у плейлист. (так, перший відповідно не повинен.)
class Playlist:
pass
Рішення
class Playlist:
def __init__(self, songs): # 1 аргумент - список із треками
self.songs = songs
def add(self, song):
if song not in self.songs:
self.songs.append(song)
def remove(self, song):
if song in self.songs:
self.songs.remove(song)
def reverse(self):
self.songs.reverse()
def __len__(self):
return len(self.songs)
def __bool__(self):
return bool(self.songs)
# або
# class Playlist:
# def __init__(self, *songs): # треки як окремі аргументи
# self.songs = list(songs) # просто вирішив нагадати про *args
# # решта така сама.
class CustomPlaylist(Playlist):
def add(self, song):
self.songs.append(song)
Іменований CustomPlaylist.
#Допрацюйте попередній клас CustomPlaylist, додавши йому власний __init__, який бере базову поведінку __init__ батьківського класу, але також створює ім’я для плейлиста в атрибуті name
class CustomPlaylist(Playlist):
def __init__???
???
def add(self, song):
self.songs.append(song)
Рішення
class CustomPlaylist(Playlist):
def __init__(self, songs, name):
super().__init__(songs)
self.name = name
def add(self, song):
self.songs.append(song)
def __str__(self):
return f"Playlist: {self.name}"
MagicBox із випадковим предметом.
#Давайте повернемося до класу Box (код нижче є), давайте від нього наслідуємося і зробимо клас MagicBox . Єдина додаткова поведінка — це те, що під час створення об’єкта коробки в ній з’являтиметься випадковий предмет зі списку. Список можливих випадкових предметів визначте де завгодно, це не важливо, хоч у глобальній області. Заготовка:
from random import choice # функція випадкового вибору з послідовності
class Box:
def __init__(self):
self.items = []
self.items_count = len(self.items)
def add(self, item):
self.items.append(item)
self.items_count = len(self.items)
def remove(self, item):
if item in self.items:
self.items.remove(item)
self.items_count = len(self.items)
else:
print("Кот из коробки говорит: Такого тут нет, досвидания!")
def __len__(self):
return self.items_count
things = ["Шапка", "Зонтик", "Кружка"]
class MagicBox(Box):
def __init__(self):
...
Рішення
from random import choice # функція випадкового вибору з послідовності
class Box:
def __init__(self):
self.items = []
self.items_count = len(self.items)
def add(self, item):
self.items.append(item)
self.items_count = len(self.items)
def remove(self, item):
if item in self.items:
self.items.remove(item)
self.items_count = len(self.items)
else:
print("Кот из коробки говорит: Такого тут нет, досвидания!")
def __len__(self):
return self.items_count
things = ["Шапка", "Зонтик", "Кружка"]
class MagicBox(Box):
def __init__(self):
super().__init__()
self.items.append(choice(things))
self.items_count = len(self.items)
# АБО:
class MagicBox(Box):
def __init__(self):
super().__init__()
self.add(choice(things)) # так крутіше
x = MagicBox()
print(x.items)