weakref
— Слабые ссылки¶
Исходный код: Lib/weakref.py
Модуль weakref
позволяет программисту на Python создавать объекты weak references to.
В дальнейшем термин referent означает объект, на который ссылается слабая ссылка.
Слабой ссылки на объект недостаточно, чтобы сохранить объект живым: когда единственными оставшимися ссылками на референт являются слабые ссылки, garbage collection может уничтожить референт и повторно использовать его память для чего-то другого. Однако до тех пор, пока объект фактически не будет уничтожен, слабая ссылка может возвращать объект, даже если на него нет сильных ссылок.
Основное применение слабых ссылок заключается в реализации кэшей или отображений, содержащих большие объекты, когда желательно, чтобы большой объект не сохранялся живым только потому, что он появляется в кэше или отображении.
Например, если у вас есть несколько больших двоичных объектов image, вы можете захотеть связать имя с каждым из них. Если бы вы использовали словарь Python для сопоставления имен с изображениями или изображений с именами, объекты image оставались бы активными только потому, что они отображались в словарях как значения или ключи. Классы WeakKeyDictionary
и WeakValueDictionary
, предоставляемые модулем weakref
, являются альтернативой, используя слабые ссылки для построения отображений, которые не поддерживают жизнь объектов только потому, что они отображаются в объектах отображения. Если, например, объект image является значением в WeakValueDictionary
, то, когда последние оставшиеся ссылки на этот объект image являются слабыми ссылками, содержащимися в слабых отображениях, сборка мусора может вернуть объект, а соответствующие ему записи в слабых отображениях просто удаляются.
WeakKeyDictionary
и WeakValueDictionary
используют слабые ссылки в своей реализации, настраивая функции обратного вызова для слабых ссылок, которые уведомляют слабые словари, когда ключ или значение были восстановлены при сборке мусора. WeakSet
реализует интерфейс set
, но сохраняет слабые ссылки на его элементы, точно так же, как это делает WeakKeyDictionary
.
finalize
предоставляет простой способ зарегистрировать функцию очистки, которая будет вызываться при сборе мусора с объекта. Это проще в использовании, чем настройка функции обратного вызова для необработанной слабой ссылки, поскольку модуль автоматически гарантирует, что финализатор будет активен до тех пор, пока объект не будет собран.
Большинство программ должны прийти к выводу, что использование одного из этих типов слабых контейнеров или finalize
- это все, что им нужно. Обычно нет необходимости создавать свои собственные слабые ссылки напрямую. Модуль weakref
предоставляет доступ к низкоуровневому оборудованию для более широкого использования.
Не на все объекты можно ссылаться слабо. К объектам, которые поддерживают слабые ссылки, относятся экземпляры классов, функции, написанные на Python (но не на C), методы экземпляров, наборы, замороженные наборы, некоторые объекты типа file objects, generators,, сокеты, массивы, декодеры, объекты шаблонов регулярных выражений и объекты кода .
Изменено в версии 3.2: Добавлена поддержка объектов thread.lock, threading.Lock и code.
Несколько встроенных типов, таких как 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__ documentation.
- class weakref.ref(object[, callback])¶
Возвращает слабую ссылку на object. Исходный объект может быть получен путем вызова ссылочного объекта, если референт все еще жив; если референт больше не жив, вызов ссылочного объекта приведет к возвращению
None
. Если указан обратный вызов, а неNone
, и возвращаемый объект weakref все еще активен, обратный вызов будет вызван, когда объект будет близок к завершению; объект слабой ссылки будет передан в качестве единственного параметра для обратного вызова; референт больше не будет использоваться. доступный.Допустимо, чтобы для одного и того же объекта создавалось много слабых ссылок. Обратные вызовы, зарегистрированные для каждой слабой ссылки, будут вызываться с самого последнего зарегистрированного обратного вызова на самый старый зарегистрированный обратный вызов.
Исключения, вызванные обратным вызовом, будут отмечены в стандартном выводе ошибок, но не могут быть распространены; они обрабатываются точно так же, как исключения, вызванные методом
__del__()
объекта.Слабыми ссылками являются hashable, если объект доступен для хэширования. Они сохранят свое значение хэша даже после удаления объекта. Если
hash()
вызывается в первый раз только после того, как объект был удален, вызов вызоветTypeError
.Слабые ссылки поддерживают проверку на равенство, но не упорядочивание. Если референты все еще активны, две ссылки имеют такое же отношение равенства, как и их референты (независимо от обратного вызова). Если какой-либо из ссылок был удален, ссылки равны только в том случае, если ссылочные объекты являются одним и тем же объектом.
Это скорее подклассифицируемый тип, чем фабричная функция.
- __callback__¶
Этот доступный только для чтения атрибут возвращает обратный вызов, связанный в данный момент с weakref. Если обратного вызова нет или если референт weakref больше не активен, то этот атрибут будет иметь значение
None
.
Изменено в версии 3.4: Добавлен атрибут
__callback__
.
- weakref.proxy(object[, callback])¶
Возвращает прокси-сервер к объекту, который использует слабую ссылку. Это поддерживает использование прокси-сервера в большинстве контекстов вместо того, чтобы требовать явного разыменования, используемого со слабыми ссылочными объектами. Возвращаемый объект будет иметь тип либо
ProxyType
, либоCallableProxyType
, в зависимости от того, является ли object вызываемым. Прокси-объекты не являются hashable независимо от референта; это позволяет избежать ряда проблем, связанных с их фундаментально изменяемой природой, и предотвращает их использование в качестве ключей словаря. обратный вызов - это то же самое, что и одноименный параметр функции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
у объектов есть дополнительный метод, который имеет те же проблемы, что и метод 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() >>>
обратный вызов - это то же самое, что и одноименный параметр функции
ref()
.Добавлено в версии 3.4.
- class weakref.finalize(obj, func, /, *args, **kwargs)¶
Возвращает вызываемый объект finalizer, который будет вызван при сборке мусора obj. В отличие от обычной слабой ссылки, финализатор всегда будет работать до тех пор, пока не будет собран ссылочный объект, что значительно упрощает управление жизненным циклом.
Финализатор считается «живым» до тех пор, пока он не будет вызван (либо явно, либо при сборке мусора), а после этого он «мертв». Вызов живого финализатора возвращает результат вычисления
func(*arg, **kwargs)
, тогда как вызов мертвого финализатора возвращаетNone
.Исключения, вызванные обратными вызовами финализатора во время сборки мусора, будут отображаться в стандартном выводе ошибок, но не могут быть распространены. Они обрабатываются так же, как исключения, вызванные методом
__del__()
объекта или обратным вызовом слабой ссылки.При завершении работы программы вызывается каждый оставшийся активный финализатор, если только для его атрибута
atexit
не было установлено значение false. Они вызываются в порядке, обратном порядку создания.Финализатор никогда не вызовет свой обратный вызов во время более поздней части interpreter shutdown, когда глобальные параметры модуля могут быть заменены на
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¶
Тип object для объектов со слабыми ссылками.
- weakref.ProxyType¶
Тип object для прокси-серверов объектов, которые не могут быть вызваны.
- weakref.CallableProxyType¶
Тип object для прокси вызываемых объектов.
- 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
заключается в том, что оно упрощает регистрацию обратного вызова без необходимости сохранения возвращаемого объекта finalizer. Например
>>> 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
во время interpreter shutdown. Таким образом, этот код должен работать на 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: ...
также не гарантируют выполнения очистки.