Недавно в заметке про управление памятью в Python мы упоминали слабые ссылки. По опросу на моем канале лишь 1 человек из 4 знал про слабые ссылки в Python, и лишь 6% читателей их применяли. Что же это такое? Слабые ссылки позволяют получать доступ к объекту, как и обычные, однако, так сказать, они не учитываются в механизме подсчета ссылок. Другими словами, слабые ссылки не могут поддерживать объект живым, если на него не осталось больше сильных ссылок.
Согласно документации, слабые ссылки нужны для организации кэшей и хэш-таблиц из «тяжелых» объектов, когда не требуется поддерживать объект живым только силами этого самого кэша; чтобы в долгоживущей программе не кончалась память из-за хранения в кэшах большого количества уже не нужных объектов.
Встроенный модуль weakref отвечает за функциональность слабых ссылок.
📎 Пример. Создаем класс Foo, сильную ссылку на его экземпляр, затем слабую ссылку, проверяем:
import weakref class Foo: ... strong_foo = Foo() weak_foo = weakref.ref(strong_foo) print(weak_foo()) # вызов слабой ссылки - доступ к исходному объекту print(weak_foo() is strong_foo) # True del strong_foo # это была последняя сильная ссылка print(weak_foo()) # None
После того, как мы избавились от единственной сильной ссылки на экземпляр класса, объект уничтожился, а слабая ссылка стала None
!
Слабые ссылки можно создавать на пользовательские классы, на set и на подклассы от dict и list, но не на сами dict и list. Встроенные типы tuple, int и подобные не поддерживают слабые ссылки (да и зачем они им?).
В weakref.ref
вторым аргументом можно передать функцию, которая будет вызвана при финализации объекта слабой ссылки:
weak_foo = weakref.ref(strong_foo, lambda r: print(f'finalizing {r}'))
weakref.getweakrefcount(object)
и weakref.getweakrefs(object)
позволяют получить количество слабых ссылок на объект и их сами.
weakref.proxy(object[, callback])
– создает слабый прокси-объект к объекту. Т.е. с ним можно обращаться также как и исходным, пока он не удалится. Попытка использовать прокси к уничтоженному объекту вызовет ReferenceError
.
Наконец, к предназначению слабых ссылок: организацию кэшей. Есть типы:
• weakref.WeakSet
– как set, но элементы хранятся по слабым ссылкам и удаляются, если на них больше нет сильных ссылок
• weakref.WeakKeyDictionary
– как dict, но КЛЮЧИ по слабым ссылкам.
• weakref.WeakValueDictionary
– как dict, но ЗНАЧЕНИЯ по слабым ссылкам.
📎 Пример:
import weakref class Foo: ... f1, f2 = Foo(), Foo() weak_dict = weakref.WeakValueDictionary() weak_dict["f1"] = f1 weak_dict["f2"] = f2 def print_weak_dict(wd): print('weak_dict: ', *wd.items()) print_weak_dict(weak_dict) # оба в словаре del f2 print_weak_dict(weak_dict) # один ушел del f1 print_weak_dict(weak_dict) # ничего не осталось
📎 Наконец, можно следить за тем, когда объект будет удален:
f = Foo() # просто установим обработчик (finalize сам никого не удаляет) weakref.finalize(f, print, "object dead or program exit") del f # а вот тут print вызовется
Специально для канала @pyway. Подписывайтесь на мой канал в Телеграм @pyway!