weakref — Слабые ссылки
Исходный код: Lib/weakref.py
Модуль weakref позволяет программисту Python создавать слабые ссылки на объекты.
В дальнейшем термин ссылочный объект означает объект, на который ссылается слабая ссылка.
Слабая ссылка на объект недостаточно для поддержания жизни объекта: когда единственные оставшиеся ссылки на ссылочный объект являются слабыми ссылками, сбор мусора может свободно уничтожить ссылочный объект и повторно использовать его память для чего-то другого. Однако до фактического уничтожения объекта слабая ссылка может возвращать объект, даже если нет сильных ссылок на него.
Основное применение слабых ссылок — реализация кэшей или отображений, содержащих большие объекты, где желательно, чтобы большой объект не сохранялся живым только потому, что он появляется в кэше или отображении.
Например, если у вас есть несколько больших бинарных объектов изображений, вы можете захотеть ассоциировать имя с каждым. Если вы использовали словарь Python для сопоставления имен с изображениями или изображений с именами, объекты изображений останутся живыми только потому, что они появляются в качестве значений или ключей в словарях. Классы WeakKeyDictionary и WeakValueDictionary, предоставляемые модулем weakref, представляют собой альтернативу, использующую слабые ссылки для построения отображений, которые не поддерживают жизнь объектов только потому, что они появляются в объектах отображения. Если, например, объект изображения является значением в WeakValueDictionary, то когда последние оставшиеся ссылки на этот объект изображения — это слабые ссылки, удерживаемые слабыми отображениями, сбор мусора может восстановить объект, а соответствующие записи в слабых отображениях просто удаляются.
WeakKeyDictionary и WeakValueDictionary используют слабые ссылки в своей реализации, настраивая функции обратного вызова на слабых ссылках, которые уведомляют слабые словари, когда ключ или значение были возвращены сбором мусора. WeakSet реализует интерфейс set, но сохраняет слабые ссылки на свои элементы, точно так же, как и WeakKeyDictionary.
finalize предоставляет прямой способ регистрации функции очистки, которая будет вызвана при сборе мусора объекта. Это проще в использовании, чем настройка функции обратного вызова на обычной слабой ссылке, так как модуль автоматически гарантирует, что finalizer останется живым до тех пор, пока объект не будет собран.
Большинству программ будет достаточно использования одного из этих типов слабых контейнеров или 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
Другие встроенные типы, такие как tuple и int, не поддерживают слабые ссылки даже при наследовании (это реализация, и может отличаться в разных реализациях Python).
Типы расширений можно легко сделать совместимыми со слабыми ссылками; см. Поддержка слабых ссылок.
-
class weakref.ref(object[, callback]) -
Возвращает слабую ссылку на object. Исходный объект можно получить, вызвав объект ссылки, если ссылочный объект все еще жив; если ссылочный объект больше не жив, вызов объекта ссылки приведет к возврату
None. Если callback предоставлен и не равенNone, и возвращаемый объект weakref все еще жив, callback будет вызван, когда объект будет готов к финализации; объект слабой ссылки будет передан в качестве единственного параметра callback; ссылочный объект больше не будет доступен.Разрешается создание многих слабых ссылок на один и тот же объект. Callback'ы, зарегистрированные для каждой слабой ссылки, будут вызываться от последнего зарегистрированного callback'а к самому старому.
Исключения, поднятые callback'ом, будут выведены в стандартный поток ошибок, но не смогут быть перехвачены; они обрабатываются точно так же, как исключения, поднятые из метода
__del__()объекта.Слабые ссылки являются хешируемыми, если object является хешируемым. Они будут сохранять свое значение хеша даже после удаления object. Если
hash()вызывается впервые только после удаления object, вызов подниметTypeError.Слабые ссылки поддерживают проверки на равенство, но не порядок. Если ссылочные объекты все еще живы, две ссылки имеют ту же связь равенства, что и их ссылочные объекты (независимо от callback). Если любой ссылочный объект был удален, ссылки равны только если объекты ссылок являются одним и тем же объектом.
Это подклассываемый тип, а не функция-фабрика.
-
__callback__ -
Это только для чтения атрибут возвращает callback, который в данный момент ассоциирован с weakref. Если callback отсутствует или ссылочный объект weakref больше не жив, то значение этого атрибута будет
None.
Изменено в версии 3.4: Добавлен атрибут
__callback__. -
-
weakref.proxy(object[, callback]) -
Возвращает прокси к object, который использует слабую ссылку. Это поддерживает использование прокси в большинстве контекстов вместо явного разыменования, используемого с объектами слабых ссылок. Возвращаемый объект будет иметь тип либо
ProxyTypeилиCallableProxyType, в зависимости от того, является ли object вызываемым. Объекты прокси не являются хешируемыми независимо от ссылочного объекта; это позволяет избежать ряда проблем, связанных с их по своей сути изменчивой природой, и предотвратить их использование в качестве ключей словарей. callback такой же, как параметр с таким же именем в функцииref().
-
weakref.getweakrefcount(object) -
Возвращает количество слабых ссылок и прокси, которые ссылаются на object.
-
weakref.getweakrefs(object) -
Возвращает список всех объектов слабых ссылок и прокси, которые ссылаются на object.
-
class weakref.WeakKeyDictionary([dict]) -
Класс отображения, который слабо ссылается на ключи. Записи в словаре будут удалены, когда больше нет сильной ссылки на ключ. Это можно использовать для ассоциации дополнительных данных с объектом, принадлежащим другим частям приложения, не добавляя атрибутов к этим объектам. Это особенно полезно для объектов, которые переопределяют доступ к атрибутам.
Примечание
Внимание: поскольку
WeakKeyDictionaryпостроен на основе словаря Python, его размер не должен меняться во время итерации по нему. Это может быть сложно гарантировать дляWeakKeyDictionary, потому что действия, выполняемые программой во время итерации, могут привести к исчезновению элементов в словаре «волшебным» образом (как побочный эффект сбора мусора).
WeakKeyDictionary объекты имеют дополнительный метод, который напрямую предоставляет внутренние ссылки. Ссылки не гарантируются как «активные» во время их использования, поэтому результат вызова ссылок необходимо проверить перед использованием. Это можно использовать для предотвращения создания ссылок, которые заставят сборщик мусора удерживать ключи дольше, чем необходимо.
-
WeakKeyDictionary.keyrefs() -
Возвращает итерируемый объект слабых ссылок на ключи.
-
class weakref.WeakValueDictionary([dict]) -
Класс отображения, который слабо ссылается на значения. Элементы в словаре будут удалены, когда больше не будет сильной ссылки на значение.
Примечание
Внимание: так как
WeakValueDictionaryпостроен на основе словаря Python, его размер не должен изменяться при итерации по нему. Это может быть сложно обеспечить дляWeakValueDictionary, так как действия, выполняемые программой во время итерации, могут привести к исчезновению элементов в словаре «волшебным образом» (как побочный эффект сбора мусора).
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 -
Свойство, которое истинно, если финализатор жив, и ложно в противном случае.
-
atexit -
Записываемое булево свойство, которое по умолчанию истинно. При выходе программы оно вызывает все оставшиеся живые финализаторы, для которых
atexitистинно. Они вызываются в обратном порядке создания.
Примечание
Важно убедиться, что func, args и kwargs не содержат ссылок на obj, ни прямо, ни косвенно, иначе obj никогда не будет собран сборщиком мусора. В частности, func не должен быть связанным методом obj.
Введено в версии 3.4.
-
-
weakref.ReferenceType -
Объект типа для объектов слабых ссылок.
-
weakref.ProxyType -
Объект типа для прокси-объектов, которые не являются вызываемыми.
-
weakref.CallableProxyType -
Объект типа для прокси-объектов вызываемых объектов.
-
weakref.ProxyTypes -
Последовательность, содержащая все объекты типа для прокси. Это может упростить проверку, является ли объект прокси, без зависимости от именования обоих типов прокси.
-
exception weakref.ReferenceError -
Исключение, возникающее при использовании прокси-объекта, но основной объект был собран. Это совпадает со стандартным исключением
ReferenceError.
См. также
- PEP 205 - Слабые ссылки
- Предложение и обоснование этой функции, включая ссылки на предыдущие реализации и информацию о похожих функциях в других языках.
1. Объекты слабых ссылок
Объекты слабых ссылок не имеют методов и атрибутов, кроме ref.__callback__. Объект слабой ссылки позволяет получить ссылку на referent, если он всё ещё существует, вызывая его:
>>> import weakref >>> class Object: ... pass ... >>> o = Object() >>> r = weakref.ref(o) >>> o2 = r() >>> o is o2 True
Если referent больше не существует, вызов объекта ссылки возвращает 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 для уменьшения накладных расходов памяти для каждой записи в отображении. Это может быть наиболее полезно для ассоциации дополнительной информации со ссылкой, но также может быть использовано для вставки дополнительной обработки при вызовах для получения referent.
Этот пример демонстрирует, как подкласс ref может быть использован для хранения дополнительной информации об объекте и влияния на возвращаемое значение при доступе к referent:
import weakref
class ExtendedRef(weakref.ref):
def __init__(self, ob, callback=None, **annotations):
super(ExtendedRef, self).__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(ExtendedRef, self).__call__()
if ob is not None:
self.__counter += 1
ob = (ob, self.__counter)
return ob
2. Пример
Этот простой пример показывает, как приложение может использовать идентификаторы объектов для получения объектов, которые оно уже встречало ранее. Идентификаторы объектов затем могут быть использованы в других структурах данных без принудительного сохранения объектов в живом состоянии, но объекты всё равно могут быть получены по идентификатору, если они существуют.
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]
3. Объекты финализатора
Основное преимущество использования finalize заключается в том, что это упрощает регистрацию обратного вызова без необходимости сохранения возвращённого объекта финализатора. Например
>>> import weakref >>> class Object: ... pass ... >>> kenny = Object() >>> weakref.finalize(kenny, print, "You killed Kenny!") #doctest:+ELLIPSIS <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() #doctest:+ELLIPSIS
(<__main__.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") #doctest:+ELLIPSIS <finalize object at ...; for 'Object' at ...> >>> exit() #doctest:+SKIP obj dead or exiting
4. Сравнение финализаторов с методами __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–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.5/library/weakref.html