from time import timeclassEpoch:def__get__(self,instance,owner):print(f"Self object {self}")print(f"Instance object {instance}")print(f"Owner object {owner}")returnint(time())classMyTime: epoch =Epoch()m =MyTime()12345678910111213141516
При вызове epoch через экземпляр m класса MyTime:
>>> m.epochSelf object<__main__.Epoch object at 0x7fcd70bf6950>Instance object<__main__.MyTime object at 0x7fcd70bf6ad0>Owner object<class'__main__.MyTime'>16865620541234567
В Self был передан сам объект класса Epoch (сам дескриптор - экземпляр дескриптора)
В Instance объект из которого произошло обращение к дескриптору (экземпляр класса MyTime)
В Owner объект, класс собственник MyTime
Мы можем обращаться к атрибуту класса (epoch) через сам класс (MyTime):
>>> MyTime.epochSelf object<__main__.Epoch object at 0x7fcd70bf6950>Instance objectNoneOwner object<class'__main__.MyTime'>16865631231234567
В Instance не был передан никакой объект - т.к. обращение произошло через класс (MyTime) а не через экземпляр (m)
Это напоминает работу @property:
Возвращать сам экземпляр класса (дескриптора), если обращение произошло через класс
Возвращать значение свойств, если обращение произошло через экземпляр
classPerson: _name ='Oleg'@propertydefname(self):return self._name# обращение через класс - вернул экземпляр класса>>> Person.name<propertyobject at 0x7fcd70bea570># обращение через экземпляр - вернул значение свойства>>>Person().name'Oleg'123456789101112131415
[!warning] Особенность хранения данных в дескипторах
Нельзя хранить данные в экземпляре дескриптора (реализующих методы get, set, delete), т.к. они делят общее состояние с экземплярами класса.
Имеется ввиду со стандартной реализацией set.
Для хранения данных в дескипторе можно определить словарь (dict) в инициализации и в методе __set__() передавать в него значение по ключу
classMyDescriptor:def__init__(self): self._values ={}def__set__(self,instance,value): self._values[instance]= valuedef__get__(self,instance,owner):if instance isNone:return selfreturn self._values.get(instance)classVector: x =MyDescriptor() y =MyDescriptor()v1 =Vector()v2 =Vector()12345678910111213141516171819
Но такой вариант реализации допустит утечку памяти - при пересохранении значений будут сохраняться ссылки на значения.
Чтобы предотвратить утечку памяти, следует использовать слабые ссылки (weakref).
v1.x =5v2.x =10Vector.x._values# {<__main__.Vector object at 0x7f621cddec10>: 5, <__main__.Vector object at 0x7f621cddec50>: 10}v3 =Vector()# вызываем верхнюю функцию для подсчёта ссылокref_count(v3)# 1v3.x =5ref_count(v3)# 2v3.y =5ref_count(v3)# 3del v3ref_count(v3)# 2# тут и происходит утечка памяти - не все ссылки на объекты были удалены