weakref
— Слабкі посилання¶
Kod źródłowy: Lib/weakref.py
Модуль weakref
дозволяє програмісту Python створювати слабкі посилання на об’єкти.
Далі термін referent означає об’єкт, на який посилається слабке посилання.
Слабкого посилання на об’єкт недостатньо, щоб зберегти об’єкт живим: коли єдині посилання на референт є слабкими посиланнями, garbage collection може знищити референт і використовувати його пам’ять для чогось іншого. Однак, поки об’єкт не буде фактично знищено, слабке посилання може повертати об’єкт, навіть якщо на нього немає сильних посилань.
Основним використанням слабких посилань є реалізація кешів або відображень, що містять великі об’єкти, де бажано, щоб великий об’єкт не зберігався живим лише тому, що він з’являється в кеші чи відображенні.
Наприклад, якщо у вас є кілька великих бінарних об’єктів зображення, ви можете призначити ім’я кожному. Якби ви використовували словник Python для зіставлення імен із зображеннями або зображень з іменами, об’єкти зображення залишалися б живими лише тому, що вони відображалися як значення або ключі в словниках. Класи WeakKeyDictionary
і WeakValueDictionary
, що надаються модулем weakref
, є альтернативою, використовуючи слабкі посилання для створення відображень, які не зберігають об’єкти лише тому, що вони з’являються в об’єктах відображення . Якщо, наприклад, об’єкт зображення є значенням у WeakValueDictionary
, тоді, коли останні посилання на цей об’єкт зображення є слабкими посиланнями, утримуваними слабкими відображеннями, збір сміття може відновити об’єкт і його відповідні записи у слабких відображеннях просто видаляються.
WeakKeyDictionary
і WeakValueDictionary
використовують слабкі посилання у своїй реалізації, встановлюючи функції зворотного виклику для слабких посилань, які сповіщають слабкі словники, коли ключ або значення було вилучено під час збирання сміття. WeakSet
реалізує інтерфейс set
, але зберігає слабкі посилання на його елементи, як це робить WeakKeyDictionary
.
finalize
забезпечує прямий спосіб реєстрації функції очищення, яка буде викликатися під час збирання сміття об’єкта. Це простіше у використанні, ніж налаштування функції зворотного виклику для необробленого слабкого посилання, оскільки модуль автоматично гарантує, що фіналізатор залишається активним, доки об’єкт не буде зібрано.
Більшість програм має виявити, що використання одного з цих слабких типів контейнерів або finalize
— це все, що їм потрібно - зазвичай не потрібно безпосередньо створювати власні слабкі посилання. Механізм низького рівня розкривається модулем weakref
для розширеного використання.
Не на все объекты можно слабо ссылаться. Объекты, поддерживающие слабые ссылки, включают экземпляры классов, функции, написанные на Python (но не на C), методы экземпляра, наборы, замороженные наборы, некоторые file object, generators, объекты типов, сокеты, массивы, деки, объекты шаблонов регулярных выражений и объекты кода.
Zmienione w wersji 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
Типи розширень можна легко створити для підтримки слабких посилань; див. Wsparcie dla słabych odniesień.
Коли для даного типу визначено __slots__
, підтримку слабких посилань вимкнено, якщо тільки рядок '__weakref__
також не присутній у послідовності рядків у декларації __slots__
. Перегляньте __slots__ документацію для деталей.
- class weakref.ref(object[, callback])¶
Повернути слабке посилання на об’єкт. Вихідний об’єкт можна отримати, викликавши еталонний об’єкт, якщо референт ще живий; якщо референт більше не живий, виклик об’єкта посилання призведе до повернення
None
. Якщо надано callback, а неNone
, і повернутий об’єкт weakref все ще живий, зворотний виклик буде викликано, коли об’єкт буде завершено; об’єкт слабкого посилання буде передано як єдиний параметр зворотного виклику; референт більше не буде доступний.Для одного об’єкта допускається створення багатьох слабких посилань. Зворотні виклики, зареєстровані для кожного слабкого посилання, викликатимуться від останнього зареєстрованого зворотного виклику до найстарішого зареєстрованого зворотного виклику.
Исключения, вызванные обратным вызовом, будут отмечены в стандартном выводе ошибок, но не могут быть распространены; они обрабатываются точно так же, как исключения, возникающие из метода
__del__()
объекта.Слабкі посилання є hashable, якщо об’єкт є хешованим. Вони збережуть своє хеш-значення навіть після видалення об’єкта. Якщо
hash()
викликається вперше лише після того, як об’єкт було видалено, виклик викличеTypeError
.Слабкі посилання підтримують тести на рівність, але не впорядкування. Якщо референти ще живі, два посилання мають таке ж відношення рівності, що й їхні референти (незалежно від зворотного виклику). Якщо будь-який референт було видалено, посилання є рівними, лише якщо об’єкти еталонів є одним і тим же об’єктом.
Це тип підкласу, а не фабрична функція.
- __callback__¶
Цей атрибут лише для читання повертає зворотний виклик, наразі пов’язаний зі слабким посиланням. Якщо зворотного виклику немає або референт слабкого посилання більше не живий, цей атрибут матиме значення
None
.
Zmienione w wersji 3.4: Додано атрибут
__callback__
.
- weakref.proxy(object[, callback])¶
Верните прокси объекту object, который использует слабую ссылку. Это поддерживает использование прокси в большинстве контекстов вместо необходимости явного разыменования, используемого со слабыми ссылочными объектами. Возвращенный объект будет иметь тип ProxyType или CallableProxyType, в зависимости от того, является ли object вызываемым. Прокси-объекты не являются хешируемыми независимо от референта; это позволяет избежать ряда проблем, связанных с их принципиально изменчивой природой, и предотвращает их использование в качестве ключей словаря. callback аналогичен одноименному параметру функции
ref()
.Доступ к атрибуту прокси-объекта после того, как референт был очищен от мусора, вызывает ошибку
ReferenceError
.Zmienione w wersji 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}
Zmienione w wersji 3.9: Додано підтримку операторів
|
і|=
, як зазначено в PEP 584.
Об’єкти WeakKeyDictionary
мають додатковий метод, який безпосередньо відкриває внутрішні посилання. Не гарантується, що посилання будуть „живими” під час їх використання, тому результат виклику посилань потрібно перевірити перед використанням. Це можна використовувати, щоб уникнути створення посилань, через які збирач сміття зберігатиме ключі довше, ніж потрібно.
- WeakKeyDictionary.keyrefs()¶
Повертає ітерацію слабких посилань на ключі.
- class weakref.WeakValueDictionary([dict])¶
Клас зіставлення, який слабко посилається на значення. Записи в словнику буде відкинуто, якщо більше не існує сильного посилання на значення.
Zmienione w wersji 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()
.Dodane w wersji 3.4.
- class weakref.finalize(obj, func, /, *args, **kwargs)¶
Повертає викликаний об’єкт фіналізатора, який буде викликаний, коли 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¶
Властивість, яка є істинною, якщо фіналізатор активний, і хибною в іншому випадку.
- atexit¶
Булева властивість, доступна для запису, яка за замовчуванням має значення true. Коли програма завершує роботу, вона викликає всі поточні фіналізатори, для яких
atexit
має значення true. Вони називаються у зворотному порядку створення.
Informacja
Важливо переконатися, що func, args і kwargs не мають жодних посилань на obj, прямо чи опосередковано, оскільки інакше obj ніколи не збиратиметься як сміття. Зокрема, func не має бути зв’язаним методом obj.
Dodane w wersji 3.4.
- weakref.ReferenceType¶
Об’єкт типу для об’єктів слабких посилань.
- weakref.ProxyType¶
Об’єкт типу для проксі об’єктів, які не можна викликати.
- weakref.CallableProxyType¶
Об’єкт типу для проксі об’єктів, що викликаються.
- weakref.ProxyTypes¶
Послідовність, що містить усі об’єкти типу для проксі. Це може спростити перевірку того, чи є об’єкт проксі-сервером, не залежачи від іменування обох типів проксі.
Zobacz także
- PEP 205 - Weak References
Пропозиція та обґрунтування цієї функції, включаючи посилання на попередні реалізації та інформацію про подібні функції іншими мовами.
Слабкі довідкові об’єкти¶
Слабкі посилальні об’єкти не мають методів і атрибутів, крім 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
Przykład¶
Цей простий приклад показує, як програма може використовувати ідентифікатори об’єктів для отримання об’єктів, які вона бачила раніше. Потім ідентифікатори об’єктів можна використовувати в інших структурах даних, не змушуючи об’єкти залишатися живими, але об’єкти все одно можна отримати за ідентифікатором, якщо вони це роблять.
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
во время 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
Визначений таким чином, наш фіналізатор отримує лише посилання на деталі, необхідні для належного очищення каталогу. Якщо об’єкт ніколи не отримує сміття, фіналізатор все одно буде викликаний при виході.
Інша перевага фіналізаторів на основі слабких рефлексів полягає в тому, що їх можна використовувати для реєстрації фіналізаторів для класів, де визначення контролює третя сторона, наприклад, запуск коду, коли модуль вивантажується:
import weakref, sys
def unloading_module():
# implicit reference to the module globals from the function body
weakref.finalize(sys.modules[__name__], unloading_module)
Informacja
Якщо ви створюєте об’єкт фіналізатора в демонічному потоці саме під час виходу з програми, тоді існує ймовірність того, що фіналізатор не буде викликаний під час виходу. Однак у демонічному потоці atexit.register()
, try: ... finally: ...
і with: ...
також не гарантують виконання очищення.