Spec-Zone.ru › Python 3.8

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, не поддерживают слабые ссылки даже при наследовании.

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

class weakref.ref(object[, callback])

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

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

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

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

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

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

__callback__

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

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

weakref.proxy(object[, callback])

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

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

weakref.getweakrefcount(object)

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

weakref.getweakrefs(object)

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

class weakref.WeakKeyDictionary([dict])

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

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

WeakKeyDictionary.keyrefs()

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

class weakref.WeakValueDictionary([dict])

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

END_OF_DOCUMENT_MARKER

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

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

Ещё одно преимущество финализаторов на основе 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–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.8/library/weakref.html

Spec-Zone.ru

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