Що таке __init__ у Python? [With Examples]

Знайомство з методом __init__ у Python для об’єктно-орієнтованого програмування

Бажаєте розпочати свій шлях у світ об’єктно-орієнтованого дизайну з Python? Зробіть перші кроки вже сьогодні, вивчивши метод __init__.

У цьому посібнику ми розглянемо основні концепції класів та об’єктів у Python, а потім заглибимось у вивчення методу __init__.

Після ознайомлення з цим матеріалом, ви зможете відповісти на такі питання:

  • Що таке змінні екземпляра, або атрибути екземпляра?
  • Яким чином метод `__init__` допомагає ініціалізувати атрибути екземпляра?
  • Як можна встановити значення за замовчуванням для атрибутів?
  • Як використовувати методи класу як конструктори для створення об’єктів?

Розпочнемо!

Класи та об’єкти у Python

Класи є основою об’єктно-орієнтованого програмування в Python. Вони дозволяють створювати структури, що об’єднують дані (атрибути) та функції (методи), які працюють з цими даними.

Створивши клас, його можна використовувати як шаблон для створення об’єктів (екземплярів).

👩‍🏫Приклад! Створимо клас `Employee`, де кожен об’єкт матиме такі атрибути:

  • `full_name`: повне ім’я співробітника (ім’я та прізвище)
  • `emp_id`: ідентифікатор співробітника
  • `department`: відділ, в якому він працює
  • `experience`: кількість років досвіду

Що це означає? 🤔

Кожен співробітник буде окремим екземпляром або об’єктом класу `Employee`. Кожен з них матиме власні значення `full_name`, `emp_id`, `department` та `experience`.

Ці атрибути також називають змінними екземпляра. Ми будемо використовувати терміни “атрибути” та “змінні екземпляра” як взаємозамінні.

Згодом ми додамо атрибути. Зараз створимо клас `Employee` наступним чином:

    class Employee:
      pass
    

Використання `pass` (заповнювача) дозволяє уникнути помилок під час виконання сценарію.

Хоча поточна версія класу `Employee` є досить простою, вона вже є дійсним класом. Отже, ми можемо створювати об’єкти класу `Employee`:

    employee_1 = Employee()
    print(employee_1)
    #Вивід: <__main__.Employee object at 0x00FEE7F0>
    

Ми також можемо додавати атрибути та ініціалізувати їх значеннями, як показано нижче:

    employee_1.full_name="Amy Bell"
    employee_1.department="HR"
    

Однак такий спосіб додавання атрибутів екземпляра неефективний та схильний до помилок. Крім того, це не дозволяє використовувати клас як шаблон для створення об’єктів. Тут на допомогу приходить метод `__init__`.

Роль методу `__init__` у класах Python

Для нас важливо мати можливість ініціалізувати змінні екземпляра під час створення об’єкта. Метод `__init__` допомагає нам у цьому. Він викликається автоматично при створенні нового об’єкта класу, для ініціалізації його атрибутів.

Якщо ви вже програмували на C++, ви побачите схожість з конструкторами.

Визначення методу `__init__`

Додамо метод `__init__` до класу `Employee`:

    class Employee:
    def __init__(self, full_name,emp_id,department,experience):
        self.full_name = full_name
        self.emp_id = emp_id
        self.department = department
        self.experience = experience
    

Параметр `self` посилається на сам екземпляр класу, а `self.attribute` ініціалізує атрибут екземпляра значенням, яке знаходиться справа.

Тепер ми можемо створювати об’єкти наступним чином:

    employee_2 = Employee('Bella Joy','M007','Marketing',3)
    print(employee_2)
    # Вивід: <__main__.Employee object at 0x017F88B0>
    

Коли ми друкуємо об’єкти співробітників, ми не отримуємо корисної інформації, крім назви класу, до якого вони належать. Давайте додамо метод `__repr__`, який визначає рядкове представлення класу:

    def __repr__(self):
        return f"{self.full_name},{self.emp_id} з {self.department}, досвід - {self.experience} років."
    

Після додавання методу `__repr__` до класу `Employee`, отримуємо:

    class Employee:
        def __init__(self, full_name,emp_id,department,experience):
          self.full_name = full_name
          self.emp_id = emp_id
          self.department = department
          self.experience = experience
      
        def __repr__(self):
            return f"{self.full_name},{self.emp_id} з {self.department}, досвід - {self.experience} років."
    

Тепер об’єкти співробітників мають корисне рядкове представлення:

    print(employee_2)
    #Вивід: Bella Joy,M007 з Marketing, досвід - 3 років.
    

Деякі правила

Перш ніж продовжувати, варто звернути увагу на кілька моментів:

  • Ми використали `self` як перший параметр в методі `__init__` для посилання на екземпляр класу. `self.attribute_name` використовується для ініціалізації атрибутів. Рекомендується використовувати `self` (хоча ви можете використовувати будь-яку іншу назву).
  • При визначенні методу `__init__` ми встановлюємо імена параметрів у визначеннях `__init__` відповідно до імен атрибутів, для покращення читабельності.

Встановлення значень за замовчуванням для атрибутів

У прикладі, який ми розглянули, всі атрибути є обов’язковими. Це означає, що створення об’єкта буде успішним, тільки якщо ми передамо значення для всіх полів у конструктор.

Спробуйте створити екземпляр класу `Employee`, не передаючи значення для атрибута `experience`:

    employee_3 = Employee('Jake Lee','E001','Engineering')
    

Ви отримаєте наступну помилку:

    Traceback (most recent call last):
      File "main.py", line 22, in <module>
        employee_3 = Employee('Jake Lee','E001','Engineering')
    TypeError: __init__() missing 1 required positional argument: 'experience'
    

Але якщо ви хочете зробити певні атрибути необов’язковими, ви можете задати значення за замовчуванням для них при визначенні методу `__init__`.

Тут ми встановлюємо значення за замовчуванням 0 для атрибута `experience`:

    class Employee:
    def __init__(self, full_name,emp_id,department,experience=0):
        self.full_name = full_name
        self.emp_id = emp_id
        self.department = department
        self.experience = experience
    
    def __repr__(self):
        return f"{self.full_name},{self.emp_id} з {self.department}, досвід - {self.experience} років."
    

Об’єкт `employee_3` створюється без значення для атрибута `experience`; для нього буде використано значення за замовчуванням 0.

    employee_3 = Employee('Jake Lee','E001','Engineering')
    print(employee_3.experience)
    #Вивід: 0
    

Альтернативні конструктори класу з використанням методів класу

Наразі ми розглянули визначення методу `__init__` і встановлення значень за замовчуванням для атрибутів. Ми також знаємо, що нам потрібно передати значення для необхідних атрибутів в конструкторі.

Однак іноді значення змінних екземпляра (атрибутів) можуть бути доступні в інших структурах даних, наприклад, у кортежі, словнику або рядку JSON.

Що ж нам тоді робити?

Розглянемо приклад. Припустимо, що значення змінних екземпляра знаходяться у словнику Python:

    dict_fanny = {'name':'Fanny Walker','id':'H203','dept':'HR','exp':2}
    

Ми можемо звернутися до словника і отримати всі атрибути наступним чином:

    name = dict_fanny['name']
    id = dict_fanny['id']
    dept = dict_fanny['dept']
    exp = dict_fanny['exp']
    

Після цього можна створити об’єкт, передаючи ці значення в конструктор класу:

    employee_4 = Employee(name, id, dept, exp)
    print(employee_4)
    #Вивід: Fanny Walker,H203 з HR, досвід - 2 років.
    

Пам’ятайте: вам доведеться робити це для кожного нового об’єкта, який ви створюєте. Такий підхід неефективний, і ми можемо зробити краще. Але як?

У Python ми можемо використовувати методи класу як конструктори для створення об’єктів класу. Щоб створити метод класу, ми використовуємо декоратор `@classmethod`.

Давайте визначимо метод, який буде аналізувати словник, отримувати значення змінних екземпляра та використовувати їх для створення об’єктів `Employee`:

    @classmethod
    def from_dict(cls,data_dict):
        full_name = data_dict['name']
        emp_id = data_dict['id']
        department = data_dict['dept']
        experience = data_dict['exp']
        return cls(full_name, emp_id, department, experience)
    

Коли нам потрібно створити об’єкти, використовуючи дані зі словника, ми можемо використовувати метод класу `from_dict()`.

💡Зверніть увагу на використання `cls` у методі класу замість `self`. Так само, як ми використовуємо `self` для посилання на екземпляр, `cls` використовується для посилання на клас. Методи класу прив’язані до класу, а не до об’єктів.

Отже, коли ми викликаємо метод класу `from_dict()` для створення об’єктів, ми викликаємо його для класу `Employee`:

    emp_dict = {'name':'Tia Bell','id':'S270','dept':'Sales','exp':3}
    employee_5 = Employee.from_dict(emp_dict)
    print(employee_5)
    #Вивід: Tia Bell,S270 з Sales, досвід - 3 років.
    

Тепер, якщо у нас є словник для кожного з n співробітників, ми можемо використовувати метод класу `from_dict()` як конструктор для створення екземплярів об’єктів, без необхідності окремо отримувати значення змінних екземпляра зі словника.

📝Примітка щодо змінних класу

Тут ми визначили метод класу, який прив’язаний до класу, а не до окремих екземплярів. Подібно до методів класу, ми також можемо мати змінні класу.

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

Часті питання

1. Навіщо потрібен метод `__init__` у Python?

Метод `__init__` у визначенні класу дозволяє нам ініціалізувати атрибути або змінні екземпляра всіх екземплярів класу. Метод `__init__` викликається кожен раз, коли створюється новий екземпляр класу.

2. Чи можна мати декілька методів `__init__` у класі Python?

Метою наявності декількох методів `__init__` у класі Python є надання кількох конструкторів, які створюють екземпляри об’єктів. Але ви не можете визначити кілька методів `__init__`. Якщо ви визначите кілька методів `__init__`, то остання реалізація перезапише попередню. Однак ви можете використовувати декоратор `@classmethod` для визначення методів класу, які можна використовувати як конструктори для створення об’єктів.

3. Що станеться, якщо не визначити метод `__init__` у класі?

Якщо ви не визначите метод `__init__`, ви все одно зможете створювати об’єкти. Однак вам доведеться вручну додавати змінні екземпляра та призначати значення кожній з них. Ви не зможете передати значення змінних екземпляра у конструктор. Це не тільки є помилковим підходом, але й нівелює мету використання класу як шаблону для створення об’єктів.

4. Чи можна використовувати значення за замовчуванням для аргументів у методі `__init__`?

Так, при визначенні методу `__init__` можна задавати значення за замовчуванням для одного або декількох атрибутів. Це допомагає зробити атрибути необов’язковими в конструкторі. Атрибути приймають значення за замовчуванням, якщо ви не передаєте їх значення в конструкторі.

5. Чи можна змінювати атрибути поза методом `__init__`?

Так, ви завжди можете оновити значення атрибута за межами методу `__init__`. Ви також можете динамічно додавати нові атрибути до екземпляра, після того, як він вже був створений.

Висновок

У цьому посібнику ми навчилися використовувати метод `__init__` для ініціалізації значень змінних екземплярів. Хоча цей механізм простий, його використання може виявитися рутинним, особливо коли у вас є багато атрибутів.

Якщо вам цікаво, ви можете дослідити модуль класів даних. У Python 3.7 та новіших версіях ви можете використовувати вбудований модуль класів даних для створення класів даних, які зберігають дані. На додаток до реалізації за замовчуванням `__init__` та інших типових методів, вони мають багато цікавих функцій для підказок типів, складних значень за замовчуванням та оптимізації.

Далі ви можете дізнатися більше про вираз `if __name__ == ‘__main__’` у Python.