Class and Object
Класи, об'єкти⚑
Клас⚑
Клас - це структура даних в Python, яка дозволяє об'єднувати дані та функції, що з ними пов'язані, в одному об'єкті.
- Клас визначає атрибути (змінні) та методи (функції), які можна використовувати для маніпуляції цими атрибутами.
- Клас є шаблоном для створення об'єктів, тобто, він описує структуру об'єкта.
- Класи дозволяють використовувати концепцію наслідування для створення нових класів, які можуть успадковувати атрибути та методи від інших класів.
- В Python, всі речі є об'єктами, і класи є способом створення цих об'єктів. Клас створює об'єкт свого власного типу.
- Об'єкти зберігаються в хіпі - heap. А референси - в стеку.
- Змінна - референс - посилання на об'єкт.
Від чого успадковуються класи і метакласи?⚑
Summary
Класи успадковуються від базового класу
object
, а метакласи - від базового метакласуtype
.
MyClass.__base__ == object # MyClass is a subclass of object.
MyMetaClass.__base__ == type # MyMetaClass is a subclass of type.
Всі класи в Python успадковуються від базового класу object
. Це означає, що object
є базовим класом для всіх класів у Python 3.
Але можна не вказувати object
, оскільки він є базовим класом за замовчуванням
Метаклас - це клас, який контролює створення і поведінку інших класів.
Метакласи в Python успадковуються від базового метакласу type
.
Приклад створення метакласу, який додає атрибут до всіх класів
class AddAttributeMeta(type):
def __init__(cls, name, bases, attrs):
attrs['status'] = 'new' # Add attribute 'status' to the class
super().__init__(name, bases, attrs)
class MyClass(metaclass=AddAttributeMeta):
pass
obj = MyClass()
print(obj.status) # "new"
Що робить метод __init__
?⚑
Метод __init__
є конструктором класу в Python. Він автоматично викликається при створенні нового об'єкта або екземпляра класу і використовується для ініціалізації початкових значень атрибутів цього об'єкта. У методі __init__
можна вказати параметри, які передаються при створенні об'єкта, і встановити їх як атрибути об'єкта. Це дозволяє підготувати об'єкт до подальшої роботи, встановити початкові стани та значення.
Що таке __new__
і чим він відрізняється від __init__
? У якій послідовності вони виконуються⚑
Summary
Основна різниця між цими двома методами полягає в тому, що
__new__
обробляє створення об'єкта, а__init__
обробляє його ініціалізацію.
__new__
викликається автоматично при виклику імені класу (при створенні екземпляра), тоді як __init__
викликається кожного разу, коли екземпляр класу повертається __new__
, передаючи повернений екземпляр у __init__
як параметр self
. Тому навіть якщо зберегти екземпляр глобально/статично та повертати його кожного разу з __new__
, для нього все одно кожного разу буде викликатися __init__
.
З цього випливає, що спочатку викликається __new__
, а потім __init__
.
Що таке self
в Python?⚑
У Python self
- це ключове слово, яке використовується для визначення екземпляра або об'єкта класу. Тобто це посилання на поточний об'єкт або екземпляр класу. Використовується для доступу до атрибутів та методів об'єкта всередині класу. Ім'я self
є конвенційним та може бути будь-яким, але зазвичай використовується саме self
для читабельності коду.
У методах класу, наприклад у методі __init__
, self
вказує на об'єкт, для якого викликається цей метод. У всіх інших методах він також передає посилання на об'єкт, який викликав цей метод, що дозволяє працювати з атрибутами та методами цього об'єкта. Іде першим параметром.
Це допомагає уникнути плутанини між локальними змінними та атрибутами класу, і дозволяє зручно та однозначно звертатися до властивостей об'єкта всередині його методів.
self
потрібно передати першим параметром, бо усі методи знаходяться в класі, а не в інстансі. Без нього клас не буде знати, до кого застосовувати метод.
Як отримати список атрибутів об'єкта⚑
Функція dir
повертає список рядків - полів об'єкта. Поле __dict__
містить словник виду {поле -> значення}
(або ж викликати vars
). dir()
повертає список імен, включаючи всі доступні атрибути, включаючи вбудовані, тоді як vars()
повертає лише атрибути, які були визначені в самому об'єкті.
Є також геттери та сеттери для отримання доступу до атрибутів.
<list> = dir(<object>) # Names of object's attributes (incl. methods).
<dict> = vars(<object>) # Dict of writable attributes. Also <obj>.__dict__.
<bool> = hasattr(<object>, '<attr_name>') # Checks if getattr() raises an AttributeError.
value = getattr(<object>, '<attr_name>') # Raises AttributeError if attribute is missing.
setattr(<object>, '<attr_name>', value) # Only works on objects with '__dict__' attribute.
delattr(<object>, '<attr_name>') # Same. Also `del <object>.<attr_name>`.
Що таке магічні методи та для чого вони потрібні⚑
Summary
Магічні методи (дандр методи, Magic methods, dundr methods) - це методи, імена яких починаються і закінчуються подвійним підкресленням. Магічні тому, що майже ніколи не викликаються явно. Їх викликають вбудовані функції або синтаксичні конструкції. Ці методи дозволяють налаштовувати поведінку об’єктів класу при взаємодії з іншими об’єктами або при виконанні певних операцій. Тобто вони дозволяють класам мати або перевизначити певну поведінку при виконанні різних операцій.
Наприклад, функція len()
викликає метод __len__()
переданого об'єкта. Метод __add__(self, other)
автоматично викликається при використанні оператора +
для додавання.
Найвживаніші магічні методи - __init__
- конструктор класу. Приймає екземпляр класу. - __new__
- приймає не instance, а об'єкт класу. Ініт вже приймає інстанс. Створює даний об'єкт, після цього на нього може відбуватись ініціалізація - __class__
- визначає клас або тип, екземпляром якого являється об'єкт. Клас і тип - різні назви одного і того ж - type(x) == x.__class__
- __name__
- імя класу - __str__, __repr__
- повертають рядкове представлення обєкту. repr - для девелопера, str - для прінта - __getattr__, __setattr__, __delattr__
- викликаються, якщо ми пробуємо знайти атрибут, якого немає - __getattribute__
- викликається при спробі отримати значення атрибуту. Якщо цей метод перевизначений, стандартний механізм пошуку значень атрибутів не буде задіяний. - __dict__
- сховище атрибутів, визначених користувачем. Пошук в ньому проводиться під час виконання і при пошуку враховується __dict__
класу обєкту і базових класів. - __bases__
- список прямих батьків - __iter__
- повертає ітератор - __eq__
- перевірка на рівність з іншим об'єктом - __add__
- додавання до іншого об'єкта - __call__
- дозволяє екземпляру класу поводитися як функція. Це означає, що об’єкт класу можна викликати як функцію з допомогою дужок
Яка різниця між __str__
та __repr__
?⚑
__str__
та __repr__
- повертають рядкове представлення об'єкту.
repr()
(representation) - повертає рядок, що представляє об'єкт у вигляді, зручному для розробника. Це те, що ми бачимо, коли об'єкт відображається на консолі Python або налагодженні. Щоб вивести для виводу об'єкт в його "представленні" (__repr__
) у рядках форматування (f-strings), використовується спеціальний форматний ключ!r
:f"Object: {obj!r}"
str()
(string) - повертає рядок, що представляє об'єкт у вигляді зручному для користувача. Це те, що друкує функція print(). Якщо не реалізований - то повертає__repr__
.
Як у класі посилатися на батьківський клас (super()
)⚑
Функція super()
використовується як метод для створення об'єкта-проксі який дозволяє доступитися до методів батьківського класу. Цей об'єкт-проксі забезпечує механізм динамічного зв'язування і дозволяє викликати методи батьківського класу з поточного класу та отримувати доступ до його атрибутів.
Конкретний об'єкт, який повертає super()
, залежить від контексту виклику. Він представляє наступний клас у ланцюжку успадкування (методи якого можуть бути викликані) після поточного класу.
При виклику методу через super()
, контекст виклику передається батьківському методу, що дозволяє коректно обробляти дані відповідно до правил успадкування.
Найчастіше super()
використовується для магічних методів. Наприклад, щоб викликати __init__()
батьківського класу.
Чи можливе множинне наслідування⚑
Так, в класах в Python можна вказати більше одного батька для похідного класу. На відміну від, наприклад, Java.
Що таке MRO⚑
Summary
MRO (Method Resolution Order) - порядок вирішення методу. Це алгоритм пошуку методу в разі, якщо у класу є два або більше батьків.
При успадкуванні класів нового стилю застосовується правило MRO (порядок вирішення методів), тобто лінійний обхід дерева класів, при цьому вкладений елемент успадкування стає доступним у атрибуті __mro__
даного класу. Такий алгоритм називається C3-лініаризація. Наслідування за правилом MRO здійснюється приблизно за наступним порядком.
- Перерахування всіх класів, успадкованих екземпляром, за правилом пошуку DFLR для класичних класів, причому клас включається в результат пошуку стільки разів, скільки він зустрічається при обході.
- Перегляд у отриманому списку дублікатів класів, з яких видаляються всі, крім останнього (останнього справа) дубліката в списку.
Обхід у глибину та зліва направо - DFLR 1. Спочатку екземпляр 2. Потім його клас 3. Далі всі суперкласи його класу з обходом спочатку у глибину, а потім зліва направо 4. Використовується перше знайдене входження.
Упорядкування за правилом MRO застосовується при успадкуванні та виклику вбудованої функції super()
, яка завжди викликає наступний клас за правилом MRO (відносно точки виклику).
Приклад успадкування в не-ромбовидних ієрархічних деревах
class D: attr = 3 # D:3 E:2
class B(D): pass # | |
class E: attr = 2 # B C:1
class C(E): attr = 1 # \ /
class A(B, C): pass # A
X = A() # |
print(X.attr) # X
>>> DFLR = [X, A, B, D, C, E]
>>> MRO = [X, A, B, D, C, E, object]
>>> Outputs string "3"
Приклад успадкування в ромбовидних ієрархічних деревах
class D: attr = 3 # D:3
class B(D): pass # / \
class C(D): attr = 1 # B C:1
class A(B, C): pass # \ /
X = A() # A
print(X.attr) # |
... # X
>>> DFLR = [X, A, B, D, C, D]
>>> MRO = [X, A, B, C, D, object] (keeps only the last duplicate D)
>>> Outputs string "1"
Що таке проблема ромба (Diamond problem)⚑
При ромбовидному успадкуванні потрібно визначити, який метод класу слід викликати. У Python вирішується за допомогою MRO.
Приклад успадкування в ромбовидних ієрархічних деревах
class D: attr = 3 # D:3
class B(D): pass # / \
class C(D): attr = 1 # B C:1
class A(B, C): pass # \ /
X = A() # A
print(X.attr) # |
... # X
>>> DFLR = [X, A, B, D, C, D]
>>> MRO = [X, A, B, C, D, object] (keeps only the last duplicate D)
>>> Outputs string "1"
Що таке міксіни?⚑
Міксін (mix-in, примішання) - це шаблон проектування в ООП, коли до ланцюжка успадкування додається невеликий допоміжний клас.
Наприклад, є клас
Тоді будь-який клас, успадкований з цим міксіном, матиме метод now()
.
У назвах міксінів зазвичай додають слово Mixin
, оскільки не існує жодного механізму для розрізнення повноцінного класу і міксіна. Міксін технічно є звичайним класом.
Чому рівний вираз object() == object()
⚑
Завжди неправда, оскільки за замовчуванням об'єкти порівнюються за полем id (адресою в пам'яті), якщо тільки не перевизначений метод __eq__
.
__slots__
⚑
Класи зберігають поля та їх значення у прихованому словнику __dict__
. Оскільки словник є змінною структурою, можна додавати та видаляти поля класу в будь-який момент. Параметр __slots__
у класі жорстко фіксує набір полів класу. Слоти використовуються, коли клас може мати дуже багато полів, наприклад, в деяких ORM або коли критична продуктивність, оскільки доступ до слоту відбувається швидше, ніж пошук у словнику, або коли в процесі виконання програми створюється мільйони екземплярів класу, застосування __slots__
дозволить економити пам'ять.
Слоти активно використовуються в бібліотеці requests
.
Мінуси: не можна присвоїти класу поле, якого немає в слотах. Не працюють методи __getattr__
і __setattr__
. Рішення: включити в __slots__
елемент __dict__
.
class Person:
__slots__ = ('name', 'age')
def __init__(self, name, age):
self.name = name
self.age = age
person = Person("John", 30)
try:
person.address = "New York" # This will raise an AttributeError
except AttributeError as e:
print("Error:", e)
person.name = "Alice" # Setting values for attributes defined in __slots__
person.age = 25
Атрибути. В чому сенс _value
, __value
⚑
Атрибути - це змінні класу. Можуть бути атрибути класу або атрибути інстансу. Атрибути класу спільні для всіх інстансів.
3 типи атрибутів - Публічні - public - a = 1
- не мають обмежень за доступом і можуть бути доступні з будь-якого місця програми. - Захищені - protected - _a = 1
- змінна чи метод не призначені для використання поза методами класу, однак атрибут доступний по цьому імені. - Приватні - private - __a = 1
- ззовні недоступний по цьому імені, тільки всередині. Але все-одно буде доступний під ім'ям a._ClassName__a
.
Public Атрибути і методи, доступні ззовні класу.
Privat Поле класу з одним ведучим підкресленням вказує на те, що параметр використовується тільки всередині класу. При цьому воно доступне для звернення ззовні. Це обмеження доступу тільки на рівні угоди.
Сучасні IDE, наприклад PyCharm
, виділять звернення до поля з підкресленням, але це не призведе до помилки в процесі виконання.
Protected Поля з подвійним підкресленням доступні всередині класу, але недоступні ззовні та недоступні нащадкам. Це досягається наступним прийомом: інтерпретатор присвоює таким полям імена у вигляді _<ClassName>__<fieldName>
. Описаний механізм називається name mangling або name decoration.
class Parent():
def __init__(self):
self.__foo = 42
class Child(Parent):
def __init__(self):
super().__init__()
Parent().__foo
>>> AttributeError: 'Parent' object has no attribute '__foo'
Parent()._Parent__foo
>>> 42
Child()._Parent__foo
>>> 42
Що таке качина типізація⚑
Summary
Качина типізація (Duck typing) - вид динамічної типізації, коли межі використання об'єкту визначаються його поточним набором методів і властивостей, на відміну від успадкування від певного класу.
Іншими словами, вважається, що об'єкт реалізує інтерфейс, якщо він містить всі методи цього інтерфейсу, незалежно від зв'язків у ієрархії успадкування та належності до якого-небудь конкретного класу.
Синоніми - неявна типізація, латентна типізація. Застосовуваної в таких мовах програмування - Perl, Smalltalk, Python, Objective-C, Ruby, JavaScript, Groovy, ColdFusion, Boo, Lua, Go, C#.
Назва терміна походить від англійського «duck test» («качиний тест»), який в оригіналі звучить так: «If it looks like a duck, swims like a duck and quacks like a duck, then it probably is a duck». («Якщо воно виглядає як качка, плаває як качка і крякає як качка, то це напевно і є качка»).
Що таке перевантаження оператора (overloading)?⚑
Overloading (перевантаження) оператора - це можливість змінювати поведінку вбудованих операторів (+, -, *
) в своєму класі . Це досягається за допомогою спеціальних методів в класах, які визначають, як оператори мають взаємодіяти з об'єктами цього класу.
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
sum_x = self.x + other.x
sum_y = self.y + other.y
return Vector(sum_x, sum_y)
v1 = Vector(1, 2)
v2 = Vector(3, 4)
result = v1 + v2
print("({0}, {1})".format(result.x, result.y)) # (4, 6)
У цьому прикладі метод __add__
перевантажено для класу Vector
, що дозволяє об'єктам цього класу використовувати оператор +
для додавання векторів.
Як створити клас з допомогою type()
⚑
Перший аргумент вказує ім'я класу, другий - кортеж базових класів - (X,)
, а третій - словник атрибутів класу.
Методи. @staticmethod
, @classmethod
⚑
Метод - це функція, яка викликається від інстансу класу. Метод зберігаються в класі, а не інстансі. Використовує параметр self
, який представляє екземпляр класу.
class MyClass:
def instance_method(self):
return f"Instance method called. Attribute: {self.attribute}"
obj = MyClass()
obj.attribute = 5
print(obj.instance_method()) # Output: Instance method called. Attribute: 5
3 типи методів - метод інстансу - тільки від інстансу. Якщо від класу - помилка. Перший параметр - інстанс self
. Цей метод має доступ до атрибутів і методів екземпляра. - метод класу @classmethod
- першим параметром приймає клас cls
. Може викликатись як від інстансу, так і від класу. - статичний метод @staticmethod
@staticmethod
Статичний метод не використовує self
або cls
. Він не має доступу до атрибутів екземпляра або класу. Це по суті функція всередині класу.
Коли використовувати: - Коли метод не потребує доступу до екземпляра або класу. - Коли метод логічно зв'язаний з класом, але не використовує його атрибути або методи.
class MyClass:
@staticmethod
def static_method():
return "Static method called"
print(MyClass.static_method()) # Output: Static method called
@classmethod
Метод класу приймає параметр cls
, який представляє сам клас. Це дозволяє методам класу доступ до атрибутів і методів класу.
Коли використовувати: - Коли метод повинен працювати з класом як з об'єктом. - Коли потрібно створити метод, що працює з загальними для всіх екземплярів даними.
class MyClass:
class_attribute = "Class attribute"
@classmethod
def class_method(cls):
return f"Class method called. Attribute: {cls.class_attribute}"
print(MyClass.class_method()) # Output: Class method called. Attribute: Class attribute
Що таке властивість (@property
)?⚑
Summary
Property (властивість) - це спеціальний метод класу, який дозволяє контролювати доступ до атрибутів об'єкта, коли вони читаються, записуються або видаляються. Property використовується для забезпечення контрольованого доступу до даних об'єкта. Реалізується з допомогою декоратора
@property
.
Property дозволяє викликати метод як атрибут і всередині заховати складну логіку (точкова нотація). Або коли змінюється логіка всередині, щоб не міняти API.
Метод property()
приймає на вхід методи get
, set
и delete
, і повертає об'єкти класу property
.
class Circle:
def __init__(self, radius):
self._radius = radius # Attribute with an underscore is a protected attribute
@property # Getter to retrieve the radius value
def radius(self):
return self._radius
@radius.setter # Setter to set the radius value
def radius(self, value):
if value < 0:
raise ValueError("Radius cannot be negative")
self._radius = value
@radius.deleter
def radius(self):
del self._radius
@property # Getter to compute the area of the circle
def area(self):
return 3.14159 * self._radius * self._radius
circle = Circle(5)
print(circle.radius) # Output: 5
circle.radius = 7
print(circle.radius) # Output: 7
print(circle.area) # Output: 153.93845
Декоратори @property
і @radius.setter
створюють property для атрибута radius
. Property radius
дозволяє отримувати і змінювати значення радіусу об'єкта circle
.
Приблизна реалізація property()
за допомогою протоколу дескрипторів
class Property:
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
self.__doc__ = doc
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError
return self.fget(obj)
def __set__(self, obj, value):
if self.fset is None:
raise AttributeError
self.fset(obj, value)
def __delete__(self, obj):
if self.fdel is None:
raise AttributeError
self.fdel(obj)
Що таке дескриптор⚑
Summary
Дескриптор - будь-який об'єкт, який визначає методи
__get__()
,__set__()
або__delete__()
. Якщо хоча б один із цих методів визначений, об'єкт стає дескриптором.
Додатково дескриптори можуть мати метод __set_name__()
. Це використовується лише у випадках, коли дескриптору потрібно знати або клас, у якому він був створений, або назву змінної класу, якій він був призначений.
Дескриптор дозволяє контролювати доступ, присвоєння і видалення атрибутів об'єктів, замість використання звичайних методів доступу.
Дескриптори працюють лише тоді, коли використовуються як змінні класу. Якщо їх поставити в інстанс, вони не мають ефекту.
Стандартна поведінка при доступі до атрибутів – отримання, встановлення та видалення атрибута зі словника об'єкта. Наприклад, a.x
має такий ланцюжок пошуку атрибуту: a.__dict__['x']
, потім у type(a).__dict__['x']
, і далі у базових класах type(a)
. Якщо шукане значення є об'єктом, що визначає один із методів дескриптора, тоді Python може замінити поведінку за замовчуванням і замість цього викликати метод дескриптора. Де це відбувається в ланцюжку пріоритетів, залежить від того, які методи дескриптора були визначені.
Є два види дескрипторів - дескриптор даних - дескриптор не даних
class DescriptorExample:
def __get__(self, instance, owner):
return instance._value
def __set__(self, instance, value):
if not isinstance(value, int):
raise ValueError("Value must be an integer.")
instance._value = value
class MyClass:
def __init__(self, value):
self._value = value
value = DescriptorExample()
obj = MyClass(42)
print(obj.value) # 42
obj.value = 100
print(obj.value) # 100
obj.value = "string" # throws ValueError
Links
- Дескриптор Інструкції - python.org
- https://webdevblog.ru/chto-takoe-deskriptory-i-ih-ispolzovanie-v-python-3-6/
- Ще трохи про дескриптори у Python
Види дескрипторів⚑
Є два види дескрипторів - дескриптор даних - дескриптор не даних
Якщо об’єкт визначає __set__()
або __delete__()
, він вважається дескриптором даних. Дескриптори, які визначають лише __get__()
, називаються дескрипторами не даних (вони часто використовуються для методів, але можливі й інші способи використання).
Функції в Python являються дескрипторами (дескрипторами не даних) - так як реалізовують __get__()
Дескриптори даних і не даних відрізняються тим, як обчислюються перевизначення щодо записів у словнику екземпляра. Якщо в словнику екземпляра є запис із таким самим іменем, як і дескриптор даних, дескриптор даних має пріоритет. Якщо в словнику екземпляра є запис із таким же ім’ям, що й дескриптор не даних, пріоритет має словниковий запис.
Щоб створити дескриптор даних лише для читання, визначте __get__()
і __set__()
з __set__()
, що викликає AttributeError
під час виклику. Щоб зробити його дескриптором даних, достатньо визначити метод __set__()
, що викликає винятки.
Абстрактні класи та методи⚑
Абстрактні класи та методи в Python забезпечують механізм визначення шаблону для класів-нащадків, змушуючи їх реалізовувати певний набір методів. Вони є ключовим елементом для забезпечення інтерфейсу та дотримання принципів об'єктно-орієнтованого програмування, таких як поліморфізм.
Абстрактний клас — це клас, який не можна створити напряму. Він використовується як базовий клас для інших. У Python абстрактні класи визначаються за допомогою модуля abc
(Abstract Base Classes).
Абстрактний метод — це метод, який визначений у базовому абстрактному класі, але не має реалізації. Класи-нащадки зобов’язані реалізувати всі абстрактні методи, інакше їх не можна створити. Для визначення абстрактного методу використовується декоратор @abstractmethod
. Абстрактні методи оголошуються всередині абстрактного класу.
Абстрактні класи та методи допомагають створювати інтерфейси та структури для класів, які мають схожу поведінку.
from abc import ABC, abstractmethod
class Shape(ABC): # Abstract class
@abstractmethod
def area(self):
"""Calculate the area of the shape."""
pass
@abstractmethod
def perimeter(self):
"""Calculate the perimeter of the shape."""
pass
class Rectangle(Shape): # Subclass implementing the abstract methods
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
shape = Shape() # Raises TypeError: Can't instantiate abstract class
rect = Rectangle(4, 5)
print("Area:", rect.area()) # Output: Area: 20
print("Perimeter:", rect.perimeter()) # Output: Perimeter: 18
Для чого метод __subclasshook__
?⚑
Метод __subclasshook__
використовується для перевірки, чи є клас підкласом певного класу. Цей метод є частиною механізму метакласів і надає можливість контролювати спадкування класів.
Коли викликається метод issubclass(cls, C)
, де cls
- поточний клас, а C - потенційний батьківський клас, Python спочатку перевіряє, чи є метод __subclasshook__
в cls
. Якщо такий метод існує, він викликається з двома аргументами: класом cls
і класом C.
Метод __subclasshook__
повинен повернути True, якщо клас cls
є підкласом класу C.
Метод __subclasshook__
можна використовувати для встановлення спеціальних вимог щодо спадкування. Наприклад, можна використовувати його, щоб перевірити, чи має підклас певні атрибути або методи, які необхідні для коректної роботи з певним інтерфейсом або механізмом.