6.1 Наслідування — це просто
Наслідування — це фундаментальна концепція об'єктно-орієнтованого програмування (ООП), яка дозволяє одному класу (званому дочірнім або підкласом) наслідувати поля і методи іншого класу (званого батьківським або суперкласом).
Такий підхід дозволяє створювати більш загальні класи та повторно використовувати код, покращуючи організацію та підтримуваність коду.
Навіщо потрібно наслідування?
Припустимо, тобі потрібно написати якийсь код, і ти вирішив зробити це у вигляді класу. Потім дізнався, що у твоєму проєкті вже існує клас, який робить майже все, що потрібно тобі у твоєму класі. Можеш просто скопіювати код цього класу у свій і користуватися собі на втіху.
А можеш «ніби скопіювати». Можеш оголосити той клас батьком свого класу, і тоді Python додасть твоєму класу поведінку класу батька.
Уяви, що ти природа і хочеш створити Собаку. Що буде швидше — створити собаку з бактерії за мільярд років або одомашнити вовка за 200 тисяч років?
Приклад базового наслідування
Припустимо, у тебе є батьківський клас Animal з полем name:
class Animal:
def __init__(self, name):
self.name = name
Хочемо створити 2 класи-нащадки для нього — Dog і Cat, а також додати їм обом метод speak:
class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"
У прикладі вище дочірні класи Dog і Cat наслідують від Animal і додають метод speak.
Клас Dog:
- Наслідує атрибут
nameзAnimal. - Додає метод
speak, що повертає рядок, специфічний для собак.
Клас Cat:
- Наслідує атрибут
nameзAnimal. - Додає метод
speak, що повертає рядок, специфічний для котів.
Фінальний варіант коду виглядає так:
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"
dog = Dog("Buddy")
cat = Cat("Whiskers")
print(dog.speak()) # Виведе: Buddy says Woof!
print(cat.speak()) # Виведе: Whiskers says Meow!
У цьому прикладі Animal є батьківським класом, а Dog і Cat — дочірніми класами. Дочірні класи наслідують атрибут name і метод __init__ від батьківського класу Animal, але додають методи speak.
6.2 Під капотом у наслідування
Якщо ти додав своєму класу батька, то це дуже схоже на те, наче ти скопіював код класу батька у свій клас.
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def __init__(self, name):
super().__init__(name) # Виклик конструктора батьківського класу
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def __init__(self, name):
super().__init__(name) # Виклик конструктора батьківського класу
def speak(self):
return f"{self.name} says Meow!"
dog = Dog("Buddy")
cat = Cat("Whiskers")
print(dog.speak()) # Виведе: Buddy says Woof!
print(cat.speak()) # Виведе: Whiskers says Meow!
Це не зовсім точний опис, але якщо ти ніколи не стикався з концепцією наслідування, то можеш поки думати про неї в такому ключі. Далі ми додамо сюди ще деталей.
6.3 Ієрархія наслідування
Дуже часто, коли проєктується складна модель великої групи класів, ти можеш зіткнутися з цілою ієрархією наслідування.
Наприклад, у тебе є клас Animal — це базовий клас для всіх тварин:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError("Subclass must implement abstract method")
Ми навіть додали йому метод speak, але так як абстрактна тварина не говорить, цей метод просто кидає виняток NotImplementedError — це стандартна практика.
Потім ми додаємо проміжні класи, які відповідають категоріям тварин: Mammal — це ссавці і Bird для птахів.
class Mammal(Animal):
def __init__(self, name, fur_color):
super().__init__(name) # Виклик конструктора батьківського класу
self.fur_color = fur_color
class Bird(Animal):
def __init__(self, name, wing_span):
super().__init__(name) # Виклик конструктора батьківського класу
self.wing_span = wing_span
def fly(self):
return f"{self.name} is flying with a wingspan of {self.wing_span} meters."
І нарешті, тільки на фінальному етапі з'являються класи конкретних видів тварин:
class Dog(Mammal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Mammal):
def speak(self):
return f"{self.name} says Meow!"
class Parrot(Bird):
def speak(self):
return f"{self.name} says Squawk!"
Ось з ними зазвичай і працює кінцевий код:
animals = [Dog("Buddy", "brown"), Cat("Whiskers", "white"), Parrot("Polly", 0.5)]
for animal in animals:
print(animal.speak())
print(f"{dog.name} has {dog.fur_color} fur.") # Виведе: Buddy has brown fur.
print(f"{cat.name} has {cat.fur_color} fur.") # Виведе: Whiskers has white fur.
print(parrot.fly()) # Виведе: Polly is flying with a wingspan of 0.5 meters.
Хоча технічно немає заборон на створення ієрархій з десятками предків, важливо пам'ятати, що без необхідності краще дотримуватися простоти. У простоті сила.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ