Слабые ссылки (weakref)

Принцип работы слабых ссылок точно такой же, как и у сильных ссылок (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

Last updated