Принцип работы слабых ссылок точно такой же, как и у сильных ссылок (strong ref), только они дополнительно учитываются сборщиком мусора
import weakref
class Person:
pass
p = Person()
w = weakref.ref(p)
print(w)
<weakref at 0x7f5b301ce610; to 'Person' at 0x7f5b300eed50>
>>> hex(id(p))
# '0x7f5b300eed50'
print(p)
<__main__.Person object at 0x7f5b300eed50>
# удаляем ссылку
del p
print(w)
<weakref at 0x7f5b301ce610; dead>
# статус слабой ссылки изменился на dead - она была удалена. Хотя сам объект пока остается в памяти
# но если попытаться вызвать объект по мертвой ссылке (dead)...
print(w())
# python вернет None - тоесть ничего
12345678910111213141516171819202122232425262728
В библиотеки (weakref) для работы со слабыми ссылками есть специальный класс для сохранения ссылок в словарь (WeakKeyDictionary())
from weakref import WeakKeyDictionary
d = weakref.WeakKeyDictionary()
d[p] = 10
d[p]
# 10
# для просмотра ссылок
d.keyrefs()
# [<weakref at 0x7f5b2e61d760; to 'type' at 0x5654f6296d40 (Person)>]
del p
d.keyrefs()
# []
123456789101112131415
[!info] Про ключи для словарей
Ключами для словарей, могут быть только хешируемые объекты! Т.е. объекты, которые реализуют метод __hash__.
Доступные дандер методы можно посмотреть в dir(obj)
Возвращаясь к примеру Data Descriptor, для устранения утечки памяти и работы дескриптора со слабыми ссылками, можно воспользоваться WeakKeyDictionary()
from weakref import WeakKeyDictionary
class MyDescriptor:
def __init__(self):
self._values = WeakKeyDictionary()
def __hash__(self):
return hash(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()
123456789101112131415161718192021222324
Проверим работу ссылок:
>>> v1.x = 10
>>> Vector.x._values.keyrefs()
[<weakref at 0x7f5e5c011b70; to 'Vector' at 0x7f5e5c0027d0>]
>>> del v1
>>> Vector.x._values.keyrefs()
[]
1234567