Документация Python неофициальный перевод
Содержание страницы

8.8. weakref – Weak references

Исходный код: Lib/weakref.py


Модуль weakref позволяет программисту Python создавать слабые ссылки на объекты.

В дальнейшем под термином referent понимается объект, на который указывает слабая ссылка.

Слабой ссылки на объект недостаточно, чтобы сохранить объект живым: когда единственные оставшиеся ссылки на референт являются слабыми ссылками, сборщик мусора может уничтожить референт и использовать его память для других целей. Однако, пока объект фактически не уничтожен, слабая ссылка может вернуть объект, даже если на него нет сильных ссылок.

Основное применение слабых ссылок – реализация кэшей или отображений, содержащих большие объекты, когда требуется, чтобы большой объект не оставался в живых только потому, что он присутствует в кэше или отображении.

Например, если у вас есть несколько крупных объектов бинарных изображений, вы можете захотеть связать имя с каждым. Если вы используете словарь Python для сопоставления имён с изображениями или изображений с именами, объекты изображений будут оставаться живыми только потому, что они появляются в качестве значений или ключей в словарях. Классы WeakKeyDictionary и WeakValueDictionary, предоставляемые модулем weakref, являются альтернативой, использующей слабые ссылки для построения отображений, которые не хранят объекты живыми только потому, что они присутствуют в объектах отображения. Если, например, объект изображения является значением в WeakValueDictionary, то, когда последние оставшиеся ссылки на этот объект изображения будут слабыми ссылками, хранящимися в слабых отображениях, сборщик мусора может освободить объект, и соответствующие записи в слабых отображениях будут просто удалены.

WeakKeyDictionary и WeakValueDictionary используют слабые ссылки в своей реализации, устанавливая функции обратного вызова на слабые ссылки, которые уведомляют слабые словари, когда ключ или значение были освобождены сборщиком мусора. Класс WeakSet реализует интерфейс set, но хранит слабые ссылки на свои элементы, подобно тому, как это делает WeakKeyDictionary.

Большинству программ достаточно использовать один из этих контейнеров слабых ссылок – обычно нет необходимости создавать собственные слабые ссылки напрямую. Низкоуровневый механизм, используемый реализациями слабых словарей, доступен через модуль weakref для опытных пользователей.

Не все объекты могут быть слабо ссылаемыми; объекты, которые могут, включают экземпляры классов, функции, написанные на Python (но не на C), методы экземпляров, множества, неизменяемые множества, некоторые объекты файлов, генераторы, объекты типов, сокеты, массивы, двусторонние очереди, объекты шаблонов регулярных выражений и объекты кода.

Изменено в версии 3.2: Добавлена поддержка thread.lock, threading.Lock и объектов кода.

Некоторые встроенные типы, такие как list и dict, напрямую не поддерживают слабые ссылки, но могут добавить поддержку через создание подклассов:

class Dict(dict):
    pass

obj = Dict(red=1, green=2, blue=3)   # этот объект поддерживает слабые ссылки

Другие встроенные типы, такие как tuple и int, не поддерживают слабые ссылки даже при создании подклассов (это деталь реализации и может отличаться в разных реализациях Python.).

Расширяемые типы можно легко научить поддерживать слабые ссылки; см. Поддержка слабых ссылок.

class weakref.ref(object[, callback])

Возвращает слабую ссылку на object. Исходный объект можно получить, вызвав объект ссылки, если референт ещё жив; если референт больше не жив, вызов объекта ссылки вернёт None. Если указан колбэк и он не равен None, и возвращённый объект слабой ссылки ещё существует, то колбэк будет вызван, когда объект будет финализирован; объект слабой ссылки будет передан колбэку как единственный параметр; референт больше не будет доступен.

Допускается создание нескольких слабых ссылок на один и тот же объект. Колбэки, зарегистрированные для каждой слабой ссылки, будут вызываться от самого недавно зарегистрированного колбэка до самого старого.

Исключения, возбуждённые колбэком, будут зафиксированы в стандартном выводе ошибок, но не могут быть распространены; они обрабатываются точно так же, как исключения, возбуждённые из метода __del__() объекта.

Слабые ссылки являются хешируемыми, если object хешируем. Они сохраняют своё хеш-значение даже после удаления object. Если hash() вызывается впервые только после удаления object, вызов возбудит TypeError.

Слабые ссылки поддерживают проверку на равенство, но не на упорядочивание. Если референты ещё живы, две ссылки имеют то же отношение равенства, что и их референты (независимо от колбэк). Если любой из референтов был удалён, ссылки равны только в том случае, если объекты ссылок являются одним и тем же объектом.

Это тип, который можно наследовать, а не фабричная функция.

weakref.proxy(object[, callback])

Return a proxy to object which uses a weak reference. This supports use of the proxy in most contexts instead of requiring the explicit dereferencing used with weak reference objects. The returned object will have a type of either ProxyType or CallableProxyType, depending on whether object is callable. Proxy objects are not hashable regardless of the referent; this avoids a number of problems related to their fundamentally mutable nature, and prevent their use as dictionary keys. callback is the same as the parameter of the same name to the ref() function.

weakref.getweakrefcount(object)

Возвращает количество слабых ссылок и прокси, которые ссылаются на object.

weakref.getweakrefs(object)

Возвращает список всех объектов слабых ссылок и прокси, которые ссылаются на 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])

Класс множества, который хранит слабые ссылки на свои элементы. Элемент будет удален, когда на него больше не останется сильных ссылок.

weakref.ReferenceType

Объект типа для объектов слабых ссылок.

weakref.ProxyType

Объект типа для прокси-объектов, которые не являются вызываемыми.

weakref.CallableProxyType

Объект типа для прокси вызываемых объектов.

weakref.ProxyTypes

Последовательность, содержащая все объекты типа для прокси. Это упрощает проверку, является ли объект прокси, без необходимости называть оба типа прокси.

exception weakref.ReferenceError

Исключение, возникающее при использовании прокси-объекта, когда исходный объект уже собран сборщиком мусора. Это то же самое, что и стандартное исключение ReferenceError.

См. также

PEP 0205 – Слабые ссылки
Предложение и обоснование этой возможности, включая ссылки на более ранние реализации и информацию о подобных возможностях в других языках.

8.8.1. Объекты слабых ссылокWeak Reference Objects

Объекты слабых ссылок не имеют атрибутов или методов, но позволяют получить референт, если он ещё существует, вызвав объект слабой ссылки:

>>> 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 – объект слабой ссылки
o = r()
if o is None:
    # объект был собран сборщиком мусора
    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(ExtendedRef, self).__init__(ob, callback)
        self.__counter = 0
        for k, v in annotations.items():
            setattr(self, k, v)

    def __call__(self):
        """Возвращает пару, содержащую объект и количество
        вызовов ссылки.
        """
        ob = super(ExtendedRef, self).__call__()
        if ob is not None:
            self.__counter += 1
            ob = (ob, self.__counter)
        return ob

8.8.2. ПримерExample

Этот простой пример показывает, как приложение может использовать идентификаторы объектов для получения объектов, которые оно уже видело ранее. Идентификаторы объектов затем можно использовать в других структурах данных, не заставляя объекты оставаться живыми, но при этом объекты всё ещё можно получить по идентификатору, если они живы.

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]