Лінеаризація
Як ми вже з'ясували, дочірній клас може не мати певного атрибута, але він може успадкувати його від базового класа. Для пошуку атрибутів в ієрархії класів використовується лінеаризація класів.
Лінеаризація — це черговість, при якій проводиться пошук зазначеного атрибута в ієрархії класів.
Використовуючи лінеаризацію відбувається пошук атрибутів в ієрархії класів. При простому успадкуванні алгоритм пошуку атрибутів виглядає наступним чином:
- якщо атрибут, до якого відбувається доступ, не знайдено в поточному класі, то виконується його пошук в базовому класі
- якщо атрибут не знайдено і в базовому класі, то виконується його пошук в базовому класі базового класа
- пошук відбувається рекурсивно аж до класа
object
- якщо атрибут не знайдено і в класі
object
, то отримуємо вийняткову ситуацію
Приклад:
>>> class SuperBase:
... def f1(self):
... print('Метод f1() класа SuperBase')
...
>>> class Base(SuperBase):
... def f2(self):
... print('Метод f2() класа Base')
...
>>> class Child(Base):
... def f2(self):
... print('Метод f2() класа Child')
... def f3(self):
... print('Метод f3() класа Child')
...
>>>
У вищенаведеному прикладі клас Child
:
- від класа
SuperBase
успадкував методf1()
- з батьківського класа метод
f2()
не успадковується, клас має власний методf2()
- має власний метод
f3()
Перевіримо на практиці:
>>> child_object = Child()
>>> child_object.f1()
Метод f1() класа SuperBase
>>> child_object.f2()
Метод f2() класа Child
>>> child_object.f3()
Метод f3() класа Child
>>>
Порядок вирішення методів
В Python лінеаризація також має назву MRO
— "Method Resolution Order",
порядок вирішення методів.
Назва може трошки вводити в оману,
тому що таким чином відбувається пошук не тільки методів, а й будь-яких атрибутів.
Лінеаризація для певного класа знаходиться в його спеціальному атрибуті __mro__
:
>>> Child.__mro__
(<class '__main__.Child'>, <class '__main__.Base'>, <class '__main__.SuperBase'>, <class 'object'>)
>>>
Але частіше користуються методом класа, який повертає не кортеж, а одразу список:
>>> Child.mro()
[<class '__main__.Child'>, <class '__main__.Base'>, <class '__main__.SuperBase'>, <class 'object'>]
>>>
Ми отримали всю ієрархію успадкування, аж до класа object
.
Перевірка об'єкта на належність класу
В Python є будована функція:
isinstance(obj, cls)
Повертає True
якщо об'єкт obj
є екземпляром класа cls
або його суперкласів.
Тобто перевірка відбувається по усій ієрархії успадкування:
>>> child_object = Child()
>>> isinstance(child_object, Child)
True
>>> isinstance(child_object, Base)
True
>>> isinstance(child_object, SuperBase)
True
>>> isinstance(child_object, object)
True
>>> isinstance(child_object, list)
False
>>>
Другим аргументом можна передати одразу декілька класів об'єднавши їх у кортеж. У цьому разі відбуватиметься перевірка належності об'єкта до ієрархій одразу декількох класів:
>>> isinstance(child_object, (Child, Base))
True
>>> isinstance(child_object, (Child, list))
True
>>> isinstance(child_object, (str, list))
False
>>> isinstance('text', (str, list))
True
>>> isinstance('text', (object, list))
True
>>>
Зауважте: функція isinstance()
використовує лінеаризацію.
Аналогічну перевірку можна виконати і так:
>>> type(child_object)
<class '__main__.Child'>
>>> type(child_object) in Child.mro()
True
>>>