# `weakref` — Слабкі посилання

Вихідний код: Lib/weakref.py

Модуль `weakref` дозволяє програмісту Python створювати слабкі посилання на об’єкти.

Далі термін referent означає об’єкт, на який посилається слабке посилання.

Слабкого посилання на об’єкт недостатньо, щоб зберегти об’єкт живим: коли єдині посилання на референт є слабкими посиланнями, garbage collection може знищити референт і використовувати його пам’ять для чогось іншого. Однак, поки об’єкт не буде фактично знищено, слабке посилання може повертати об’єкт, навіть якщо на нього немає сильних посилань.

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

Наприклад, якщо у вас є кілька великих бінарних об’єктів зображення, ви можете призначити ім’я кожному. Якби ви використовували словник Python для зіставлення імен із зображеннями або зображень з іменами, об’єкти зображення залишалися б живими лише тому, що вони відображалися як значення або ключі в словниках. Класи `WeakKeyDictionary` і `WeakValueDictionary`, що надаються модулем `weakref`, є альтернативою, використовуючи слабкі посилання для створення відображень, які не зберігають об’єкти лише тому, що вони з’являються в об’єктах відображення . Якщо, наприклад, об’єкт зображення є значенням у `WeakValueDictionary`, тоді, коли останні посилання на цей об’єкт зображення є слабкими посиланнями, утримуваними слабкими відображеннями, збір сміття може відновити об’єкт і його відповідні записи у слабких відображеннях просто видаляються.

`WeakKeyDictionary` і `WeakValueDictionary` використовують слабкі посилання у своїй реалізації, встановлюючи функції зворотного виклику для слабких посилань, які сповіщають слабкі словники, коли ключ або значення було вилучено під час збирання сміття. `WeakSet` реалізує інтерфейс `set`, але зберігає слабкі посилання на його елементи, як це робить `WeakKeyDictionary`.

`finalize` забезпечує прямий спосіб реєстрації функції очищення, яка буде викликатися під час збирання сміття об’єкта. Це простіше у використанні, ніж налаштування функції зворотного виклику для необробленого слабкого посилання, оскільки модуль автоматично гарантує, що фіналізатор залишається активним, доки об’єкт не буде зібрано.

Більшість програм має виявити, що використання одного з цих слабких типів контейнерів або `finalize` — це все, що їм потрібно - зазвичай не потрібно безпосередньо створювати власні слабкі посилання. Механізм низького рівня розкривається модулем `weakref` для розширеного використання.

Not all objects can be weakly referenced. Objects which support weak references include class instances, functions written in Python (but not in C), instance methods, sets, frozensets, some file objects, generators, type objects, sockets, arrays, deques, regular expression pattern objects, and code objects.

Змінено в версії 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 все ще живий, зворотний виклик буде викликано, коли об’єкт буде завершено; об’єкт слабкого посилання буде передано як єдиний параметр зворотного виклику; референт більше не буде доступний.

  Для одного об’єкта допускається створення багатьох слабких посилань. Зворотні виклики, зареєстровані для кожного слабкого посилання, викликатимуться від останнього зареєстрованого зворотного виклику до найстарішого зареєстрованого зворотного виклику.

  Exceptions raised by the callback will be noted on the standard error output, but cannot be propagated; they are handled in exactly the same way as exceptions raised from an object’s `__del__()` method.

  Слабкі посилання є hashable, якщо об’єкт є хешованим. Вони збережуть своє хеш-значення навіть після видалення об’єкта. Якщо `hash()` викликається вперше лише після того, як об’єкт було видалено, виклик викличе `TypeError`.

  Слабкі посилання підтримують тести на рівність, але не впорядкування. Якщо референти ще живі, два посилання мають таке ж відношення рівності, що й їхні референти (незалежно від зворотного виклику). Якщо будь-який референт було видалено, посилання є рівними, лише якщо об’єкти еталонів є одним і тим же об’єктом.

  Це тип підкласу, а не фабрична функція.

  __callback__

    Цей атрибут лише для читання повертає зворотний виклик, наразі пов’язаний зі слабким посиланням. Якщо зворотного виклику немає або референт слабкого посилання більше не живий, цей атрибут матиме значення `None`.

  Змінено в версії 3.4: Додано атрибут `__callback__`.

weakref.proxy(object[, callback])

  Return a proxy to object which uses a weak reference. This supports use of the proxy in most contexts instead of requiring the explicit dereferencing used with weak reference objects. The returned object will have a type of either `ProxyType` or `CallableProxyType`, depending on whether object is callable. Proxy objects are not hashable regardless of the referent; this avoids a number of problems related to their fundamentally mutable nature, and prevents their use as dictionary keys. callback is the same as the parameter of the same name to the `ref()` function.

  Accessing an attribute of the proxy object after the referent is garbage collected raises `ReferenceError`.

  Змінено в версії 3.8: Розширено підтримку операторів на проксі-об’єктах, щоб включити оператори множення матриці `@` і `@=`.

weakref.getweakrefcount(object)

  Повертає кількість слабких посилань і проксі, які посилаються на об’єкт.

weakref.getweakrefs(object)

  Повертає список усіх слабких посилань і проксі-об’єктів, які посилаються на об’єкт.

class weakref.WeakKeyDictionary([dict])

  Клас зіставлення, який слабко посилається на ключі. Записи в словнику буде відкинуто, коли більше немає сильного посилання на ключ. Це можна використовувати для зв’язування додаткових даних з об’єктом, що належить іншим частинам програми, без додавання атрибутів до цих об’єктів. Це може бути особливо корисним для об’єктів, які перевизначають доступ до атрибутів.

  Note that when a key with equal value to an existing key (but not equal identity) is inserted into the dictionary, it replaces the value but does not replace the existing key. Due to this, when the reference to the original key is deleted, it also deletes the entry in the dictionary:

      >>> 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 = {}

  A workaround would be to remove the key prior to reassignment:

      >>> 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: Added support for `|` and `|=` operators, specified in PEP 584.

Об’єкти `WeakKeyDictionary` мають додатковий метод, який безпосередньо відкриває внутрішні посилання. Не гарантується, що посилання будуть «живими» під час їх використання, тому результат виклику посилань потрібно перевірити перед використанням. Це можна використовувати, щоб уникнути створення посилань, через які збирач сміття зберігатиме ключі довше, ніж потрібно.

WeakKeyDictionary.keyrefs()

  Повертає ітерацію слабких посилань на ключі.

class weakref.WeakValueDictionary([dict])

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

  Змінено в версії 3.9: Додано підтримку операторів `|` і `|=`, як зазначено в PEP 584.

`WeakValueDictionary` objects have an additional method that has the same issues as the `WeakKeyDictionary.keyrefs()` method.

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 is the same as the parameter of the same name to the `ref()` function.

  Нове в версії 3.4.

class weakref.finalize(obj, func, /, *args, **kwargs)

  Повертає викликаний об’єкт фіналізатора, який буде викликаний, коли obj буде зібрано сміття. На відміну від звичайного слабкого посилання, фіналізатор завжди житиме, доки не буде зібрано еталонний об’єкт, що значно спрощує керування життєвим циклом.

  Фіналізатор вважається живим, доки його не викликають (явним чином або під час збирання сміття), а після цього він мертвий. Виклик активного фіналізатора повертає результат обчислення `func(*arg, **kwargs)`, тоді як виклик мертвого фіналізатора повертає `None`.

  Exceptions raised by finalizer callbacks during garbage collection will be shown on the standard error output, but cannot be propagated. They are handled in the same way as exceptions raised from an object’s `__del__()` method or a weak reference’s callback.

  Коли програма завершує роботу, викликається кожен живий фіналізатор, що залишився, якщо його атрибут `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

    Властивість, яка є істинною, якщо фіналізатор активний, і хибною в іншому випадку.

  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

## Comparing finalizers with `__del__()` methods

Припустимо, ми хочемо створити клас, екземпляри якого представляють тимчасові каталоги. Каталоги мають бути видалені разом із їхнім вмістом, коли станеться перша з наступних подій:

- об’єкт вивезено сміття,

- the object’s `remove()` method is called, or

- програма виходить.

We might try to implement the class using a `__del__()` method as follows:

    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()

Starting with Python 3.4, `__del__()` methods no longer prevent reference cycles from being garbage collected, and module globals are no longer forced to `None` during interpreter shutdown. So this code should work without any issues on CPython.

However, handling of `__del__()` methods is notoriously implementation specific, since it depends on internal details of the interpreter’s garbage collector implementation.

Більш надійною альтернативою може бути визначення фіналізатора, який посилається лише на певні функції та об’єкти, які йому потрібні, а не має доступу до повного стану об’єкта:

    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: ...` також не гарантують виконання очищення.