Spec-Zone.ru › Python 3.10

weakref — Слабые ссылки

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

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

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

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

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

Например, если у вас есть несколько больших бинарных изображений, вы можете захотеть ассоциировать имя с каждым из них. Если вы используете словарь Python для сопоставления имен с изображениями или изображений с именами, объекты изображений останутся живыми только потому, что они появляются в качестве значений или ключей в словарях. Классы WeakKeyDictionary и WeakValueDictionary, предоставляемые модулем weakref, представляют собой альтернативу, использующую слабые ссылки для построения отображений, которые не поддерживают жизнь объектов только потому, что они появляются в объектах отображения. Например, если объект изображения является значением в WeakValueDictionary, то при попадании в 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 все еще жив, callback будет вызван, когда объект будет готов к завершению; объект слабой ссылки будет передан в качестве единственного параметра в callback; ссылочный объект больше недоступен.

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

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

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

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

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

__callback__

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

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

weakref.proxy(object[, callback])

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

Обращение к атрибуту объекта прокси после того, как ссылочный объект собран мусором, вызывает ReferenceError.

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

weakref.getweakrefcount(object)

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

weakref.getweakrefs(object)

Возвращает список всех объектов слабых ссылок и прокси, которые ссылаются на 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 объекты имеют дополнительный метод, у которого те же проблемы, что и у метода 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)

Возвращает объект вызываемого финализатора, который будет вызван при сборе мусора 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.10/library/weakref.html

Spec-Zone.ru

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