weakref — Слабые ссылки
Исходный код: Lib/weakref.py
Модуль weakref позволяет программисту Python создавать слабые ссылки на объекты.
В дальнейшем термин ссылочное значение означает объект, на который ссылается слабая ссылка.
Слабая ссылка на объект недостаточно для поддержания жизни объекта: когда единственные оставшиеся ссылки на ссылочное значение — слабые ссылки, сбор мусора свободен уничтожить ссылочное значение и повторно использовать его память для чего-то другого. Однако до фактического уничтожения объекта слабая ссылка может вернуть объект, даже если на него нет сильных ссылок.
Основное применение слабых ссылок — реализация кэшей или отображений, содержащих большие объекты, где нежелательно, чтобы большой объект сохранялся только потому, что он отображается в кэше или отображении.
Например, если у вас есть несколько больших бинарных объектов изображений, вы можете захотеть сопоставить имя с каждым. Если вы используете словарь Python для сопоставления имен с изображениями или изображений с именами, объекты изображений останутся живыми только потому, что они появляются в качестве значений или ключей в словарях. Классы WeakKeyDictionary и WeakValueDictionary, предоставляемые модулем weakref, представляют собой альтернативу, используя слабые ссылки для построения отображений, которые не сохраняют объекты живыми только потому, что они появляются в объектах отображения. Если, например, объект изображения является значением в WeakValueDictionary, то когда последние оставшиеся ссылки на этот объект изображения — слабые ссылки, удерживаемые слабыми отображениями, сбор мусора может вернуть объект, и соответствующие записи в слабых отображениях просто удаляются.
WeakKeyDictionary и WeakValueDictionary используют слабые ссылки в своей реализации, настраивая функции обратного вызова на слабых ссылках, которые уведомляют слабые словари, когда ключ или значение были возвращены сборщиком мусора. WeakSet реализует интерфейс set, но сохраняет слабые ссылки на свои элементы, как и WeakKeyDictionary.
finalize предоставляет прямой способ регистрации функции очистки, которая будет вызываться при сборе мусора объекта. Это проще в использовании, чем настройка функции обратного вызова на исходной слабой ссылке, так как модуль автоматически гарантирует, что финализатор остается активным до сбора объекта.
Большинству программ будет достаточно использования одного из этих типов контейнеров со слабыми ссылками или finalize — обычно нет необходимости создавать собственные слабые ссылки напрямую. Низкоуровневые механизмы раскрываются модулем weakref для использования в сложных случаях.
Не все объекты могут быть слабо ссылаемыми; к таким объектам относятся экземпляры классов, функции, написанные на Python (но не на C), методы экземпляров, множества, замороженные множества, некоторые объекты файлов, генераторы, объекты типов, сокеты, массивы, очереди, объекты шаблонов регулярных выражений и объекты кода.
Изменено в версии 3.2: Добавлена поддержка thread.lock, threading.Lock и объектов кода.
Некоторые встроенные типы, такие как list и dict, не поддерживают слабые ссылки напрямую, но могут добавить поддержку через наследование:
class Dict(dict):
pass
obj = Dict(red=1, green=2, blue=3) # this object is weak referenceable
Подробность реализации CPython: Другие встроенные типы, такие как tuple и int, не поддерживают слабые ссылки даже при наследовании.
Типы расширений легко могут быть адаптированы для поддержки слабых ссылок; см. Поддержка слабых ссылок.
Когда __slots__ определены для данного типа, поддержка слабых ссылок отключена, если строка '__weakref__' также присутствует в последовательности строк в объявлении __slots__. См. документацию __slots__ для получения подробностей.
-
class weakref.ref(object[, callback]) -
Возвращает слабую ссылку на object. Исходный объект можно получить, вызвав объект ссылки, если ссылочное значение всё ещё существует; если ссылочное значение больше не существует, вызов объекта ссылки вызовет возвращение
None. Если callback предоставлен и не равенNone, и возвращаемый объект weakref всё ещё существует, функция обратного вызова будет вызвана при приближении к завершению объекта; объект слабой ссылки будет передан как единственный параметр в функцию обратного вызова; ссылочное значение больше не будет доступно.Допустимо создание многих слабых ссылок для одного объекта. Функции обратного вызова, зарегистрированные для каждой слабой ссылки, будут вызываться от наиболее недавно зарегистрированной функции обратного вызова к самой старой.
Исключения, поднятые функцией обратного вызова, будут записаны в стандартный поток ошибок, но не могут быть распространены; они обрабатываются точно так же, как исключения, поднятые из метода
__del__()объекта.Слабые ссылки являются хешируемыми, если объект object хешируем. Они будут сохранять своё значение хэша даже после удаления object. Если
hash()вызывается в первый раз только после удаления object, вызов подниметTypeError.Слабые ссылки поддерживают проверки на равенство, но не порядок. Если ссылочные значения всё ещё существуют, две ссылки имеют ту же связь равенства, что и их ссылочные значения (независимо от callback). Если ссылочное значение одного из них было удалено, ссылки равны только в том случае, если объекты ссылок являются одним и тем же объектом.
Это подкласс, а не функция-фабрика.
-
__callback__ -
Это только для чтения атрибут возвращает функцию обратного вызова, текущую связанную со слабой ссылкой. Если нет функции обратного вызова или если ссылочное значение слабой ссылки больше не существует, этот атрибут будет иметь значение
None.
Изменено в версии 3.4: Добавлен атрибут
__callback__. -
-
weakref.proxy(object[, callback]) -
Возвращает прокси к object, использующему слабую ссылку. Это позволяет использовать прокси в большинстве контекстов вместо явного разыменования, используемого с объектами слабых ссылок. Возвращаемый объект будет иметь тип либо
ProxyTypeлибоCallableProxyType, в зависимости от того, является ли object вызываемым. Объекты-прокси не являются хешируемыми независимо от ссылочного значения; это устраняет множество проблем, связанных с их фундаментально изменчивой природой, и предотвращает их использование в качестве ключей словарей. callback аналогичен параметру с тем же именем в функцииref().Изменено в версии 3.8: Расширена поддержка операторов на объектах-прокси, чтобы включить операторы матричного умножения
@и@=.
-
weakref.getweakrefcount(object) -
Возвращает количество слабых ссылок и прокси, которые ссылаются на object.
-
weakref.getweakrefs(object) -
Возвращает список всех объектов слабых ссылок и прокси, которые ссылаются на object.
-
class weakref.WeakKeyDictionary([dict]) -
Класс отображения, который слабо ссылается на ключи. Записи в словаре будут удалены, когда больше не будет сильной ссылки на ключ. Это можно использовать для привязки дополнительных данных к объекту, управляемому другими частями приложения, без добавления атрибутов к этим объектам. Это особенно полезно для объектов, которые переопределяют доступ к атрибутам.
Изменено в версии 3.9: Добавлена поддержка операторов
|и|=, указанных в PEP 584.
WeakKeyDictionary объекты имеют дополнительный метод, который напрямую раскрывает внутренние ссылки. Гарантируется, что ссылки не будут «активными» в момент их использования, поэтому результат вызова ссылок необходимо проверять перед использованием. Это можно использовать для предотвращения создания ссылок, которые заставят сборщик мусора удерживать ключи дольше, чем необходимо.
-
WeakKeyDictionary.keyrefs() -
Возвращает итерируемый объект слабых ссылок на ключи.
-
class weakref.WeakValueDictionary([dict]) -
Класс отображения, который ссылается на значения слабо. Записи в словаре будут удалены, когда больше не будет сильной ссылки на значение.
Изменено в версии 3.9: Добавлена поддержка операторов
|и|=, как указано в PEP 584.
WeakValueDictionary объекты имеют дополнительный метод, у которого те же проблемы, что и у метода keyrefs() объектов WeakKeyDictionary.
-
WeakValueDictionary.valuerefs() -
Возвращает итерируемый объект слабых ссылок на значения.
-
class weakref.WeakSet([elements]) -
Класс множества, который хранит слабые ссылки на свои элементы. Элемент будет удалён, когда больше не будет сильной ссылки на него.
-
class weakref.WeakMethod(method) -
Специализированный подкласс
ref, который имитирует слабую ссылку на связанный метод (то есть метод, определённый в классе и просматриваемый в экземпляре). Поскольку связанный метод является временным, стандартная слабая ссылка не может удерживать его.WeakMethodимеет специальный код для восстановления связанного метода до тех пор, пока не умрёт либо объект, либо исходная функция:>>> class C: ... def method(self): ... print("method called!") ... >>> c = C() >>> r = weakref.ref(c.method) >>> r() >>> r = weakref.WeakMethod(c.method) >>> r() <bound method C.method of <__main__.C object at 0x7fc859830220>> >>> r()() method called! >>> del c >>> gc.collect() 0 >>> r() >>>Новое в версии 3.4.
-
class weakref.finalize(obj, func, /, *args, **kwargs) -
Возвращает вызываемый объект finalizer, который будет вызван при сборе мусора объекта obj. В отличие от обычной слабой ссылки, finalizer всегда выживет до тех пор, пока не будет собран объект ссылки, что значительно упрощает управление жизненным циклом.
Finalizer считается активным до тех пор, пока он не вызывается (явно или при сборе мусора), а после этого он неактивный. Вызов активного finalizer возвращает результат вычисления
func(*arg, **kwargs), а вызов неактивного finalizer возвращаетNone.Исключения, возникающие при вызове finalizer во время сбора мусора, будут отображаться на стандартном выходе ошибок, но не могут быть перехвачены. Они обрабатываются так же, как исключения, возникающие из метода
__del__()объекта или обратного вызова слабой ссылки.При выходе программы каждый оставшийся активный finalizer вызывается, если его атрибут
atexitне установлен в false. Они вызываются в обратном порядке создания.Finalizer никогда не вызовет свой обратный вызов на поздней стадии завершения интерпретатора, когда значения глобальных переменных модуля могут быть заменены на
None.-
__call__() -
Если self активен, то он помечается как неактивный, и возвращается результат вызова
func(*args, **kwargs). Если self неактивен, то возвращаетсяNone.
-
detach() -
Если self активен, то он помечается как неактивный, и возвращается кортеж
(obj, func, args, kwargs). Если self неактивен, то возвращаетсяNone.
-
peek() -
Если self активен, то возвращается кортеж
(obj, func, args, kwargs). Если self неактивен, то возвращаетсяNone.
-
alive -
Свойство, которое равно true, если finalizer активен, и false в противном случае.
-
atexit -
Записываемое булевое свойство, которое по умолчанию равно true. При выходе программы оно вызывает все оставшиеся активные finalizer, для которых
atexitравно true. Они вызываются в обратном порядке создания.
Примечание
Важно убедиться, что func, args и kwargs не содержат ссылок на obj, ни напрямую, ни косвенно, так как в противном случае obj никогда не будет собран сборщиком мусора. В частности, func не должен быть связанным методом obj.
Новое в версии 3.4.
-
-
weakref.ReferenceType -
Объект типа для объектов слабых ссылок.
-
weakref.ProxyType -
Объект типа для прокси-объектов, которые не являются вызываемыми.
-
weakref.CallableProxyType -
Объект типа для прокси-объектов вызываемых объектов.
-
weakref.ProxyTypes -
Последовательность, содержащая все типы объектов для прокси. Это может упростить проверку, является ли объект прокси, не завися от именования обоих типов прокси.
См. также
- PEP 205 - Слабые ссылки
-
Предложение и обоснование этой функции, включая ссылки на предыдущие реализации и информацию о похожих функциях в других языках.
Объекты слабых ссылок
Объекты слабых ссылок не имеют методов и атрибутов, кроме ref.__callback__. Объект слабой ссылки позволяет получить ссылку на сославшийся объект, если он всё ещё существует, вызвав его:
>>> import weakref >>> class Object: ... pass ... >>> o = Object() >>> r = weakref.ref(o) >>> o2 = r() >>> o is o2 True
Если сославшийся объект больше не существует, вызов объекта ссылки возвращает None:
>>> del o, o2 >>> print(r()) None
Проверка того, что объект слабой ссылки всё ещё активен, должна выполняться с помощью выражения ref() is not None. Обычно код приложения, которому требуется использовать объект ссылки, должен следовать этому шаблону:
# r is a weak reference object
o = r()
if o is None:
# referent has been garbage collected
print("Object has been deallocated; can't frobnicate.")
else:
print("Object is still live!")
o.do_something_useful()
Использование отдельной проверки «активности» создаёт проблемы с гонками в многопоточных приложениях; другой поток может сделать слабую ссылку недействительной до вызова слабой ссылки; показанный выше шаблон безопасен как в многопоточных, так и в однопоточных приложениях.
Специализированные версии объектов ref могут быть созданы путём наследования. Это используется в реализации WeakValueDictionary для уменьшения накладных расходов памяти для каждой записи в отображении. Это может быть полезно для ассоциации дополнительной информации с ссылкой, но также может быть использовано для вставки дополнительной обработки при вызовах для получения сослающегося объекта.
Этот пример демонстрирует, как подкласс ref может быть использован для хранения дополнительной информации об объекте и влиять на значение, возвращаемое при обращении к сослающемуся объекту:
import weakref
class ExtendedRef(weakref.ref):
def __init__(self, ob, callback=None, /, **annotations):
super().__init__(ob, callback)
self.__counter = 0
for k, v in annotations.items():
setattr(self, k, v)
def __call__(self):
"""Return a pair containing the referent and the number of
times the reference has been called.
"""
ob = super().__call__()
if ob is not None:
self.__counter += 1
ob = (ob, self.__counter)
return ob
Пример
Этот простой пример демонстрирует, как приложение может использовать идентификаторы объектов для извлечения объектов, которые оно уже встречало. Идентификаторы объектов можно использовать в других структурах данных, не заставляя объекты оставаться активными, но объекты всё ещё можно получить по идентификатору, если они существуют.
import weakref
_id2obj_dict = weakref.WeakValueDictionary()
def remember(obj):
oid = id(obj)
_id2obj_dict[oid] = obj
return oid
def id2obj(oid):
return _id2obj_dict[oid]
Объекты finalizer
Главное преимущество использования finalize заключается в том, что это упрощает регистрацию обратного вызова, без необходимости сохранения возвращённого объекта finalizer. Например,
>>> import weakref >>> class Object: ... pass ... >>> kenny = Object() >>> weakref.finalize(kenny, print, "You killed Kenny!") <finalize object at ...; for 'Object' at ...> >>> del kenny You killed Kenny!
Finalizer также может быть вызван непосредственно. Однако finalizer вызовет обратный вызов не более одного раза.
>>> def callback(x, y, z):
... print("CALLBACK")
... return x + y + z
...
>>> obj = Object()
>>> f = weakref.finalize(obj, callback, 1, 2, z=3)
>>> assert f.alive
>>> assert f() == 6
CALLBACK
>>> assert not f.alive
>>> f() # callback not called because finalizer dead
>>> del obj # callback not called because finalizer dead
Вы можете отменить регистрацию finalizer, используя метод detach(). Это делает finalizer неактивным и возвращает аргументы, переданные конструктору при его создании.
>>> obj = Object()
>>> f = weakref.finalize(obj, callback, 1, 2, z=3)
>>> f.detach()
(<...Object object ...>, <function callback ...>, (1, 2), {'z': 3})
>>> newobj, func, args, kwargs = _
>>> assert not f.alive
>>> assert newobj is obj
>>> assert func(*args, **kwargs) == 6
CALLBACK
Если вы не установите атрибут atexit в False, finalizer будет вызван при выходе программы, если он всё ещё активен. Например
>>> obj = Object() >>> weakref.finalize(obj, print, "obj dead or exiting") <finalize object at ...; for 'Object' at ...> >>> exit() obj dead or exiting
Сравнение финализаторов с методами __del__()
Предположим, мы хотим создать класс, экземпляры которого представляют временные каталоги. Каталоги должны удаляться вместе с их содержимым, когда происходит одно из следующих событий:
- объект удаляется сборщиком мусора,
- вызывается метод
remove()объекта, или - программа завершается.
Мы можем попытаться реализовать класс, используя метод __del__() следующим образом:
class TempDir:
def __init__(self):
self.name = tempfile.mkdtemp()
def remove(self):
if self.name is not None:
shutil.rmtree(self.name)
self.name = None
@property
def removed(self):
return self.name is None
def __del__(self):
self.remove()
Начиная с Python 3.4, методы __del__() больше не препятствуют тому, чтобы циклы обращений к памяти удалялись сборщиком мусора, а глобальные переменные модуля больше не принудительно устанавливаются в None во время завершения интерпретатора. Таким образом, этот код должен работать без проблем в CPython.
Однако обработка методов __del__() в значительной степени зависит от реализации, так как она зависит от внутренних деталей реализации сборщика мусора интерпретатора.
Более надёжный вариант — определить финализатор, который ссылается только на необходимые функции и объекты, а не на весь состояние объекта:
class TempDir:
def __init__(self):
self.name = tempfile.mkdtemp()
self._finalizer = weakref.finalize(self, shutil.rmtree, self.name)
def remove(self):
self._finalizer()
@property
def removed(self):
return not self._finalizer.alive
Определённый таким образом, наш финализатор получает ссылку только на те данные, которые необходимы для надлежащей очистки каталога. Если объект никогда не удаляется сборщиком мусора, финализатор всё равно будет вызван при выходе из программы.
Другое преимущество финализаторов на основе weakref заключается в том, что их можно использовать для регистрации финализаторов для классов, определение которых контролируется третьей стороной, например, для запуска кода при разгрузке модуля:
import weakref, sys
def unloading_module():
# implicit reference to the module globals from the function body
weakref.finalize(sys.modules[__name__], unloading_module)
Примечание
Если вы создаёте объект финализатора в демоническом потоке непосредственно перед завершением программы, существует вероятность того, что финализатор не будет вызван при выходе. Однако в демоническом потоке atexit.register(), try: ... finally: ... и with: ... не гарантируют выполнение очистки.
© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.9/library/weakref.html