Можливо, навіть далеко не всі чули, що таке дескриптори, але точно всі використовували їх
Я кажу це так впевнено, оскільки @property — є дескриптором 😮

Вступ

Оригінально, цей клас не задумувався як декоратор, а лише як явний транслятор між атрибутом та набором методів — геттер, сеттер та делеттер
Виглядати це має ось так:

# https://docs.python.org/uk/3/library/functions.html#property
class C:
    def __init__(self):
        self._x = None

    def getx(self):
        return self._x

    def setx(self, value):
        self._x = value

    def delx(self):
        del self._x

    x = property(getx, setx, delx, "I'm the 'x' property.")

І “під капотом” у @property відбувається вся магія
Як зробити власний аналог — описано тут. Я ж хочу спробувати пояснити дескриптори простішим чином 😉

class CountDescriptor:
    def __init__(self, base_count=0):
        self.count = base_count

    def __get__(self, instance, owner):
        if instance is None:  # зчитуємо атрибут класу
            return self.count
        return self.count + instance.count


class Container:
    total = CountDescriptor(10)

    def __init__(self, count=15):
        self.count = count

print(Container.total)
print(Container().total)
print(Container(50).total)

Виглядає складно? 😨

Нічьо, зараз розберемось 😎

Клас CountDescriptor має метод __get__, отже є дескриптором
У цього класу також є атрибут count, що задається у конструкторі

Якщо атрибут (у нас — total) є дескриптором, то при спробі зчитати атрибут, Пайтон виконає у нього метод __get__, передавши першим аргументом об’єкт, а другим — клас цього об’єкту
Якщо ж зчитується атрибут класу, як перший атрибут буде передано None

У самому методі ми перевіримо це, і змінимо логіку:

  1. якщо зчитується атрибут класу, то результатом у методі буде власний атрибут count

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

Тепер перейдемо до класу Container
Цей клас має звичайний атрибут count, зі значенням за замовчуванням 10, у конструкторі
І головне, що має цей клас — атрибут-дескриптор: total

Отже:

  • для атрибуту total класу Container виконається умова 1,
    відповідно значення буде 10

  • для атрибуту total об’єкту Container() виконається умова 2,
    відповідно значення буде 25 (10 + 15)

  • для атрибуту total об’єкту Container(50) виконається умова 2,
    відповідно значення буде 60 (10 + 50)

Якщо все ж складно, можете погратись з цим кодом у моєму JupyterLite, або продебагати код і побачити як усі дії відбуваються наяву 🤔

Продебагати онлайн можна у Python Tutor

Усе те саме працює і для зміни значення атрибуту (__set__) та видалення атрибуту (__delete__). Якщо хоча б один з цих методів є у класі, його можна використовувати як дескриптор 🤓

Для чого ж можна використовувати дескриптори у реальному світі?)

Для всього 👀
Тобто, це всього лиш інструмент 😅
Він допомагає приховати складну реалізацію, при цьому залишивши дуже зручний інструмент у вигляді атрибуту

З поширених прикладів, це поля БД у ORM (зміна атрибута у об’єкті потім повпливає на зміни у БД), вищезгадані @property, а також його друзі @classmethod та @staticmethod, і навіть самі методи як такі 🤯
Це все прямим текстом пише в документації

За допомогою дескрипторів можна просто реалізувати кешування атрибутів, які можуть рідко використовуватись

А ще, якби це дивно не виглядало, методи дескриптора можуть бути асинхронними, і це відкриває ще більше можливостей, звісно зі своєю ложкою дьогтю у вигляді заплутаного використання 🥲

Післямови не буде, бо я такого не вмію 😢

Якщо вам подобається такий контент, підписуйтесь на мій телеграм-канал:
Python просто | з Коропом

Поділись своїми ідеями в новій публікації.
Ми чекаємо саме на твій довгочит!
Вітя
Вітя@kor0p

Python SDET @ ajax.systems

832Прочитань
10Автори
18Читачі
Підтримати
На Друкарні з 14 квітня

Більше від автора

Вам також сподобається

  • Програмування

    Чи цікавитеся ви програмуванням? Як ви вивчали його? Програмування — це не лише навичка, яка може допомогти вам знайти роботу в сучасному цифровому світі. Це також потужний інструмент для розвитку мислення, творчості та розв’язання складних завдань.

    Теми цього довгочиту:

    Програмування
  • Java. Чому локи потрібно тримати якомога менший час?

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

    Теми цього довгочиту:

    Java

Коментарі (0)

Підтримайте автора першим.
Напишіть коментар!

Вам також сподобається

  • Програмування

    Чи цікавитеся ви програмуванням? Як ви вивчали його? Програмування — це не лише навичка, яка може допомогти вам знайти роботу в сучасному цифровому світі. Це також потужний інструмент для розвитку мислення, творчості та розв’язання складних завдань.

    Теми цього довгочиту:

    Програмування
  • Java. Чому локи потрібно тримати якомога менший час?

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

    Теми цього довгочиту:

    Java