Spec-Zone.ru › Python 3.11

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])

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

Разрешается создание нескольких слабых ссылок на один и тот же объект. Функции обратного вызова, зарегистрированные для каждой слабой ссылки, будут вызваны от самой недавно зарегистрированной функции обратного вызова до самой старой.

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

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

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

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

__callback__

Это только для чтения атрибут возвращает функцию обратного вызова, текущую ассоциированную со слабой ссылкой. Если нет функции обратного вызова или если ссылка—цель weakref больше не существует, этот атрибут будет иметь значение None.

Изменено в версии 3.4: Добавлен атрибут __callback__.

weakref.proxy(object[, callback])

Возвращает прокси для объекта, использующий слабую ссылку. Это поддерживает использование прокси в большинстве контекстов вместо явного разыменования, используемого с объектами слабых ссылок. Возвращённый объект будет иметь тип либо ProxyType либо CallableProxyType, в зависимости от того, является ли объект вызываемым. Объекты-прокси не являются хешируемыми, независимо от ссылки—цели; это позволяет избежать ряда проблем, связанных с их принципиально изменяемой природой, и предотвращает их использование в качестве ключей словарей. callback такой же, как параметр с таким же названием для функции ref().

Обращение к атрибуту объекта-прокси после того, как ссылка—цель собрана мусором, поднимет ReferenceError.

Изменено в версии 3.8: Расширена поддержка операторов на объектах-прокси, чтобы включить операторы матричного умножения @ и @=.

weakref.getweakrefcount(object)

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

weakref.getweakrefs(object)

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

class weakref.WeakKeyDictionary([dict])

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

Обратите внимание, что при вставке ключа с равным значением существующему ключу (но не равной идентичностью) в словарь, он заменяет значение, но не заменяет существующий ключ. Из-за этого, когда ссылка на исходный ключ удаляется, это также удаляет запись в словаре:

>>> class T(str): pass
...
>>> k1, k2 = T(), T()
>>> d = weakref.WeakKeyDictionary()
>>> d[k1] = 1   # d = {k1: 1}
>>> d[k2] = 2   # d = {k1: 2}
>>> del k1      # d = {}

Решение проблемы заключалось бы в удалении ключа перед переприсвоением:

>>> class T(str): pass
...
>>> k1, k2 = T(), T()
>>> d = weakref.WeakKeyDictionary()
>>> d[k1] = 1   # d = {k1: 1}
>>> del d[k1]
>>> d[k2] = 2   # d = {k2: 2}
>>> del k1      # d = {k2: 2}

Изменено в версии 3.9: Добавлена поддержка операторов | и |=, указанных в PEP 584.

WeakKeyDictionary объекты имеют дополнительный метод, который напрямую отображает внутренние ссылки. Ссылки не гарантируется, что они будут «живыми» в момент их использования, поэтому результат вызова ссылок необходимо проверить перед использованием. Это можно использовать для предотвращения создания ссылок, которые заставят сборщик мусора удерживать ключи дольше, чем необходимо.

WeakKeyDictionary.keyrefs()

Возвращает итерируемый объект слабых ссылок на ключи.

class weakref.WeakValueDictionary([dict])

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

Изменено в версии 3.9: Добавлена поддержка операторов | и |= в соответствии с PEP 584.

WeakValueDictionary объекты имеют дополнительный метод, у которого те же проблемы, что и у метода WeakKeyDictionary.keyrefs().

WeakValueDictionary.valuerefs()

Возвращает итерируемый объект слабых ссылок на значения.

class weakref.WeakSet([elements])

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

class weakref.WeakMethod(method[, callback])

Специализированный подкласс 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()
>>>

callback — это то же самое, что и параметр с таким же именем в функции ref().

Добавлен в версии 3.4.

class weakref.finalize(obj, func, /, *args, **kwargs)

Возвращает объект вызываемого финализатора, который будет вызван при сборке мусора obj. В отличие от обычной слабой ссылки, финализатор всегда выживет до тех пор, пока объект ссылки не будет собран, что значительно упрощает управление жизненным циклом.

Финализатор считается активным до тех пор, пока не будет вызван (либо явно, либо при сборке мусора), а после этого он неактивен. Вызов активного финализатора возвращает результат вычисления func(*arg, **kwargs), в то время как вызов неактивного финализатора возвращает None.

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

При завершении программы каждый оставшийся активный финализатор вызывается, если атрибут atexit не был установлен в false. Они вызываются в обратном порядке создания.

Финализатор никогда не вызовет свой обработчик во второй части завершения интерпретатора, когда модульные глобальные переменные могут быть заменены 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, если финализатор активен, и false в противном случае.

atexit

Записываемое булево свойство, которое по умолчанию равно true. При завершении программы, оно вызывает все оставшиеся активные финализаторы, для которых 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]

Объекты-финализаторы

Основное преимущество использования finalize заключается в том, что он упрощает регистрацию обратного вызова без необходимости сохранения возвращаемого объекта-финализатора. Например

>>> 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!

Финализатор также можно вызвать непосредственно. Однако финализатор вызовет обратный вызов не более одного раза.

>>> 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

Вы можете отменить регистрацию финализатора, используя его метод detach(). Это уничтожает финализатор и возвращает аргументы, переданные в конструктор при его создании.

>>> 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, финализатор будет вызван при выходе программы, если он всё ещё активен. Например

>>> 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

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

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

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–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.11/library/weakref.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API