Spec-Zone.ru › Python 3.13

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

Spec-Zone.ru

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