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
Другие встроенные типы, такие как tuple и int, не поддерживают слабые ссылки даже при наследовании (это реализационная деталь и может отличаться в различных реализациях Python).
Типы расширений могут легко поддерживать слабые ссылки; см. Поддержка слабых ссылок.
-
class weakref.ref(object[, callback]) -
Возвращает слабую ссылку на объект. Исходный объект может быть получен, вызвав объект ссылки, если целевой объект все еще жив; если целевой объект больше не жив, вызов объекта ссылки приведет к возвращению
None. Если callback предоставлен и не равенNone, и возвращённый объект weakref все ещё жив, функция обратного вызова будет вызвана, когда объект собирается быть уничтожен; объект слабой ссылки будет передан в качестве единственного параметра в функцию обратного вызова; целевой объект больше не будет доступен.Разрешается создание многих слабых ссылок на один и тот же объект. Функции обратного вызова, зарегистрированные для каждой слабой ссылки, будут вызываться от самой недавно зарегистрированной функции обратного вызова до старейшей зарегистрированной функции обратного вызова.
Исключения, возбуждаемые функцией обратного вызова, будут отмечены в стандартном выходном потоке ошибок, но не могут быть перехвачены; они обрабатываются так же, как исключения, возбуждаемые методом
__del__()объекта.Слабые ссылки являются хешируемыми, если объект хешируем. Они будут сохранять свое значение хэша даже после удаления объекта. Если функция
hash()будет вызвана впервые только после удаления объекта, вызов возбудитTypeError.Слабые ссылки поддерживают проверки на равенство, но не упорядочения. Если целевые объекты все еще живы, две ссылки имеют такую же связь равенства, как и их целевые объекты (независимо от callback). Если какой-либо целевой объект был удалён, ссылки равны только если объекты ссылок являются одним и тем же объектом.
Это подклассируемый тип, а не функция-фабрика.
-
__callback__ -
Это только для чтения атрибут возвращает функцию обратного вызова, в настоящее время связанную со weakref. Если нет функции обратного вызова или если целевой объект weakref больше не жив, то значение этого атрибута будет
None.
Изменено в версии 3.4: Добавлен атрибут
__callback__. -
-
weakref.proxy(object[, callback]) -
Возвращает прокси к объекту, использующему слабую ссылку. Это поддерживает использование прокси в большинстве контекстов вместо явного разыменования, используемого с объектами слабых ссылок. Возвращаемый объект будет иметь тип либо
ProxyTypeилиCallableProxyType, в зависимости от того, является ли объект вызываемым. Объекты прокси не являются хешируемыми независимо от целевого объекта; это позволяет избежать многих проблем, связанных с их фундаментально изменяемой природой, и предотвращает их использование в качестве ключей словарей. callback такой же, как параметр с таким же именем в функцииref().
-
weakref.getweakrefcount(object) -
Возвращает количество слабых ссылок и прокси, которые ссылаются на объект.
-
weakref.getweakrefs(object) -
Возвращает список всех объектов слабых ссылок и прокси, которые ссылаются на объект.
-
class weakref.WeakKeyDictionary([dict]) -
Класс отображения, который слабо ссылается на ключи. Записи в словаре будут удалены, когда на ключ больше нет сильной ссылки. Это можно использовать для связывания дополнительных данных с объектом, принадлежащим другим частям приложения, без добавления атрибутов к этим объектам. Это особенно полезно для объектов, которые переопределяют доступ к атрибутам.
Примечание
Осторожно: так как
WeakKeyDictionaryпостроен на основе словаря Python, его размер не должен изменяться во время итерации по нему. Это может быть сложно гарантировать дляWeakKeyDictionary, так как действия, выполняемые программой во время итерации, могут привести к исчезновению элементов в словаре «волшебным» образом (как побочный эффект сбора мусора).
WeakKeyDictionary объекты имеют дополнительный метод, который напрямую предоставляет внутренние ссылки. Ссылки не гарантируют, что они будут «живыми» в момент использования, поэтому результат вызова ссылок необходимо проверять перед использованием. Это можно использовать для предотвращения создания ссылок, которые заставят сборщик мусора хранить ключи дольше, чем необходимо.
-
WeakKeyDictionary.keyrefs() -
Возвращает итерируемый объект слабых ссылок на ключи.
-
class weakref.WeakValueDictionary([dict]) -
Класс отображения, который ссылается на значения слабо. Записи в словаре будут удалены, когда больше не будет сильной ссылки на значение.
Примечание
Внимание: поскольку
WeakValueDictionaryпостроен на основе словаря Python, его размер не должен изменяться при итерации. Это может быть сложно обеспечить дляWeakValueDictionary, так как действия, выполняемые программой во время итерации, могут привести к исчезновению элементов в словаре «волшебным» образом (как побочный эффект сбора мусора).
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в ложь. Они вызываются в обратном порядке создания.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 -
Свойство, которое истинно, если finalizer жив, и ложно в противном случае.
-
atexit -
Письмовое булево свойство, которое по умолчанию истинно. При выходе программы вызываются все оставшиеся живые finalizers, для которых
atexitистинно. Они вызываются в обратном порядке создания.
Примечание
Важно убедиться, что func, args и kwargs не содержат ссылок на obj, ни напрямую, ни косвенно, иначе obj никогда не будет собран сборщиком мусора. В частности, func не должен быть связанным методом obj.
Новое в версии 3.4.
-
-
weakref.ReferenceType -
Объект типа для слабых ссылок.
-
weakref.ProxyType -
Объект типа для псевдонимов объектов, которые не вызываемы.
-
weakref.CallableProxyType -
Объект типа для псевдонимов вызываемых объектов.
-
weakref.ProxyTypes -
Последовательность, содержащая все объекты типов псевдонимов. Это может упростить проверку, является ли объект псевдонимом, не завися от именования обоих типов псевдонимов.
-
exception weakref.ReferenceError -
Исключение, генерируемое при использовании объекта псевдонима, но базовый объект был собран. Это такое же исключение, как стандартное исключение
ReferenceError.
См. также
- PEP 205 - Слабые ссылки
-
Предложение и обоснование этой функции, включая ссылки на предыдущие реализации и информацию о похожих функциях в других языках.
8.8.1. Объекты слабых ссылок
Объекты слабых ссылок не имеют методов и атрибутов, кроме ref.__callback__. Объект слабой ссылки позволяет получить ссылку на referent, если он всё ещё существует, вызвав его:
>>> import weakref >>> class Object: ... pass ... >>> o = Object() >>> r = weakref.ref(o) >>> o2 = r() >>> o is o2 True
Если referent больше не существует, вызов объекта ссылки возвращает 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 для уменьшения накладных расходов памяти для каждой записи в отображении. Это может быть наиболее полезно для добавления дополнительной информации к ссылке, но также может быть использовано для вставки дополнительной обработки при вызовах для получения referent.
Этот пример показывает, как подкласс ref может использоваться для хранения дополнительной информации об объекте и влияния на значение, возвращаемое при обращении к referent:
import weakref
class ExtendedRef(weakref.ref):
def __init__(self, ob, callback=None, **annotations):
super(ExtendedRef, self).__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(ExtendedRef, self).__call__()
if ob is not None:
self.__counter += 1
ob = (ob, self.__counter)
return ob
8.8.2. Пример
Этот простой пример показывает, как приложение может использовать идентификаторы объектов для получения объектов, которые оно уже встречало. Идентификаторы объектов затем могут быть использованы в других структурах данных без принудительного поддержания жизни объектов, но объекты всё ещё могут быть получены по идентификатору, если они существуют.
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]
8.8.3. Объекты 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()
(<__main__.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
END_OF_DOCUMENT_MARKER
```Если вы не установите атрибут atexit в значение False, финализатор будет вызван при завершении программы, если он всё ещё жив. Например
>>> obj = Object() >>> weakref.finalize(obj, print, "obj dead or exiting") <finalize object at ...; for 'Object' at ...> >>> exit() obj dead or exiting
8.8.4. Сравнение финализаторов с методами __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–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.6/library/weakref.html