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