Задача "Дескриптор для отслеживания изменения значения" на Python

Задача 4:

Напишите дескриптор, который будет отслеживать изменения значения атрибута. При каждом изменении нужно сохранять старое значение в список изменений.

Пример использования:

class ChangeTracker:
    pass

class Product:
    price = ChangeTracker()

p = Product()
p.price = 100  # первое значение
p.price = 120  # второе значение
p.price = 150  # третье значение

# Список изменений: [100, 120]

Почему мой код вызывает ошибку?

class ChangeTracker:
    def __set_name__(self, owner, name):
        self.name = "__" + name
        self.changes = []

    def __get__(self, instance, owner):
        return getattr(instance, self.name)

    def __set__(self, instance, value):
        self.changes.append(value)
        setattr(instance, self.name, value)

    def get_changes(self):
        return self.changes


class Product:
    price = ChangeTracker()

    def __init__(self, price) -> None:
        self.price = price

    # Метод для получения изменений
    def get_price_changes(self):
        return Product.price.get_changes()  # Доступ через класс, а не через self.price


# Пример использования
p = Product(90)
p.price = 100  # первое изменение
p.price = 120  # второе изменение
print(p.price)
p.price = 150  # третье изменение

print(p.price)
# Получаем список изменений
# print(p.get_price_changes())  # Список изменений: [90,100, 120]

ошибка:

Traceback (most recent call last):
  File "d:\обучение\stepik 28\tz.py", line 179, in <module>
    print(p.get_price_changes())  # Список изменений: [90,100, 120]
          ^^^^^^^^^^^^^^^^^^^^^
  File "d:\обучение\stepik 28\tz.py", line 167, in get_price_changes
    return Product.price.get_changes()  # Доступ через класс, а не через self.price
           ^^^^^^^^^^^^^
  File "d:\обучение\stepik 28\tz.py", line 149, in __get__
    return getattr(instance, self.name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute '__price'

Ответы (1 шт):

Автор решения: Alex Titov

См. про аргументы __get__ в доке

    def __get__(self, instance, owner):
        return self if instance is None else getattr(instance, self.name)
→ Ссылка