ООП

З Java Ви вже знаєте, що об’єктно-орієнтоване програмування — це парадигма програмування, що полягає у використанні об’єктів та класів для написання структури програм. Відомо, що ООП полягає у принципах інкапсуляції, успадкування, поліморфізму та абстракції.

Інкапсуляція

У Python досить простий синтаксис створення класів — ключове слово і назва класу: class <назва>:. Створення об’єктів натомість виконується у стилі виклику назви класу як функції: уявіть, що відбувається виклик конструктора: <назва класу>(<аргументи>).

Класи містять змінні (атрибути) та функції (методи). Ось тільки для останніх першим завжди треба вказувати параметр self, навіть якщо функція не прийматиме аргументів. Цей параметр є заміною this з Java та використовується для доступу до членів класу. У присвоєнні self. завжди важливо вказувати, адже інакше буде присвоєно значення новій локальній змінній! self як перший параметр — це особливість мови. Python передаватиме об’єкт класу у цей параметр.

class Cat: # Клас "Кішка"
    fur_color = None # Атрибут "колір шерсті"
    def voice(self): # Метод "голос"
        print("Meow")
maple = Cat() # Створення об'єкта кішки Мейпл
maple.fur_color = "tortie" # Встановлення черепахового кольору шерсті
maple.voice() # Зробити "няв" :3

У Пайтоні методи, які мають вигляд __назва__ називають “магічними методами”. Замість конструкторів тут використовують магічний метод __init__(self, <...>). Він може бути тільки один, тому варто визначати значення аргументів за замовчуванням, якщо потрібно “декілька конструкторів”. Також не забувайте, що першим параметром має бути self.

Ще одним магічним методом є __str__(self) — визначення репрезентації об’єкта у вигляді рядка. Відповідає методу toString() у Java.

class Cat:
    #...
    def __init__(self, color):
        self.fur_color = color
    def __str__(self):
        return f"A {self.fur_color} cat."
#...
marble = Cat("calico")
print(marble) # "A calico cat."

Не дуже часто потрібним методом є деструктор __del__(self). Він викликається автоматично одразу перед тим, як об’єкт буде видаленим (вручну або через невикористання).

Модифікатори доступу?

На відміну від інших мов, ООП у Python працює дещо інакше. Модифікаторів тут немає, але є інші способи вказати область доступу атрибутів і методів (членів класу). Приватні члени класу просто мають вигляд __назва. Захищених взагалі немає, але за звичаєм їх позначають _назва.

class Cat:
    __brain = 0
    _tag = ""
    def get_tag(self):
        return _tag

Успадкування

Для створення дочірнього класу треба після його назви вказати у дужках назву батьківського класу: class <назва_дочірнього>(<назва_батьківського>). Наслідуються усі члени класу, навіть конструктор, окрім приватних.

Конструктор або будь-які методи дочірнього класу можна викликати звичайним способом за назвою класу або викликати super(), що приблизно поверне посилання на батьківський клас. У конструкторі дочірнього класу завжди варто викликати конструктор батьківського, адже Python не викликає його автоматично. Чому?

У цій мові ООП працює дещо по-іншому, ніж в інших мовах програмування. Класи є не просто “шаблонами об’єктів”, а вони є частково “прототипами”, а точніше, “способом групування даних і функціоналу в одному місці”. Тому створення дочірнього класу просто копіює члени батьківського класу, але не зв’язує його з ним.

class Black_cat(Cat):
    def __init__(self):
        super().__init__("black")

Множинне успадкування у Python дозволене — можна прописати кілька батьківських класів через кому.

Поліморфізм

Поліморфізм у Python здебільшого досягається перевизначенням, або заміщенням методів. Як динамічна мова, він не буде попереджувати про можливість помилок якщо класи не мають спільного суперкласу, тому в цілому, можливо навіть створювати незалежні класи з однаковими методами і використовувати немовби вони перевизначені, хоча насправді такими не є.

class Animal:
    def voice(self):
        pass
class Cat(Animal):
    def voice(self):
        print("Няв!")
class Dog(Animal):
    def voice(self):
        print("Гав!")
dog = Dog()
dog.voice()
cat = Cat()
cat.voice()

Де статичні члени, абстракні класи, інтерфейси?

Як динамічна мова програмування, Python просто не потребує використання інтерфейсів та абстрактних класів, тому у звичній Вам “справжній формі” їх немає.

Будь-які атрибути, а також методи, які власне не мають першого параметра self, можна вважати “статичними”. Доступ до них забезпечуватиметься як з об’єктів, так і з імені самого класу. Якщо змінити атрибут через об’єкт, то він буде змінений лише у цьому об’єкті, а якщо через клас — для класу. У Пайтоні класи також є об’єктами — ця ширша концепція називається метакласами, але для основ мови це не буде розглянуто у даному курсі.

class Cat:
    number_of_cats = 0
    def __init__(self):
        Cat.number_of_cats += 1
    def how_many_cats():
        print(Cat.number_of_cats)
Cat.how_many_cats() # 0
cat1 = Cat()
cat2 = Cat()
Cat.how_many_cats() # 2

До того ж, унікальною особливістю мови, що дозволяє змінювати властивості методів та атрибутів, наприклад, вказувати явно статичні методи, є декоратори. Це не менш велика тема, яку я вважаю не зовсім базовою, тому рекомендую почитати про них у додаковому джерелі: Python: Decorators in OOP.


Copyleft 🄯 2020–2024 Михайло Стецюк <yaBobJonez@gmail.com>.
Сайт має ліцензію Creative Commons License.
Дякую рідній школі та вчителям!