Виклик дескрипторів
Оновлено: 28.04.2023
Загалом, дескриптор — це атрибут об’єкта з «зв’язуючою поведінкою», доступ до атрибутів якого перевизначено методами в протоколі дескриптора: __get__(), __set__() і __delete__(). Якщо будь-який із цих методів визначено для об’єкта, він називається дескриптором.
Поведінка за умовчанням для доступу до атрибутів полягає в отриманні, установці або видаленні атрибута зі словника об’єкта. Наприклад, a.x має ланцюжок пошуку, який починається з a.__dict__['x'], потім type(a).__dict__['x'] і продовжується через базові класи типу type(a), за винятком метакласів.
Проте, якщо шукане значення є об’єктом, що визначає один із методів дескриптора, тоді Python може замінити поведінку за замовчуванням і замість цього викликати метод дескриптора. Де це відбувається в ланцюжку пріоритетів, залежить від того, які методи дескриптора були визначені та як вони були викликані.
Початковою точкою для виклику дескриптора є прив’язка, a.x. Спосіб збирання аргументів залежить від a:
Прямий дзвінок Найпростіший і найменш поширений виклик — це коли код користувача безпосередньо викликає метод дескриптора: x.__get__(a). Прив’язка екземпляра У разі прив’язки до екземпляра об’єкта a.x перетворюється на виклик: type(a).__dict__['x'].__get__(a, type(a)). Прив’язка класу Якщо прив’язується до класу, A.x перетворюється на виклик: A.__dict__['x'].__get__(None, A). Супер прив’язка A dotted lookup such as super(A, a).x searches a.__class__.__mro__ for a base class B following A and then returns B.__dict__['x'].__get__(a, A). If not a descriptor, x is returned unchanged.
Наприклад, прив’язки, пріоритет виклику дескриптора залежить від того, які методи дескриптора визначено. Дескриптор може визначати будь-яку комбінацію __get__(), __set__() та __delete__(). Якщо він не визначає __get__(), тоді доступ до атрибута поверне сам об’єкт дескриптора, якщо немає значення в словнику екземплярів об’єкта. Якщо дескриптор визначає __set__() та/або __delete__(), це дескриптор даних; якщо він не визначає жодного, це дескриптор не даних. Зазвичай дескриптори даних визначають і __get__(), і __set__(), тоді як дескриптори без даних мають лише метод __get__(). Дескриптори даних із визначеними __get__() і __set__() (і/або __delete__()) завжди замінюють перевизначення в словнику екземпляра. Навпаки, дескриптори, не пов’язані з даними, можуть бути перевизначені екземплярами.
Методи Python (включаючи ті, що прикрашені @staticmethod і @classmethod) реалізовані як дескриптори не даних. Відповідно, екземпляри можуть перевизначати та замінювати методи. Це дозволяє окремим примірникам набувати поведінки, яка відрізняється від інших примірників того самого класу.
Функція property() реалізована як дескриптор даних. Відповідно, екземпляри не можуть перевизначати поведінку властивості.