Weak references в Python: что выведет код?
@python_quizРазберем этот квиз
Короткий фрагмент кода:

Что здесь происходит и почему вывод будет именно таким.
Краткое объяснение поведения
- weakref.ref создаёт «слабую» ссылку на объект — она не увеличивает счётчик ссылок (reference count).
- В CPython объекты управляются подсчётом ссылок: когда счётчик ссылок объекта становится нулём, объект немедленно освобождается (в обычных условиях).
- После del obj мы удаляем последнюю сильную (именованную) ссылку на экземпляр A. Поскольку слабая ссылка не удерживает объект, объект будет уничтожен, и вызов ref() вернёт None.
Поэтому вывод программы (в CPython) будет:

Первый print: ref() возвращает сам объект, и проверка идентичности is obj даёт True. Второй print: после удаления obj и разрушения объекта ref() возвращает None, и проверка ref() is None даёт True.
Разбор вариантов (почему другие ответы неверны)
- "True, затем True" — соответствует поведению CPython в этом простом случае.
- "True, затем False" — означало бы, что объект остался жив после удаления имени obj, что возможно только если существует ещё какая-то сильная ссылка (например, другая переменная указывала на тот же объект) или если сборщик не удалил объект немедленно (см. ниже про разные реализации).
- "False, затем True" — означало бы, что сразу после создания слабой ссылки ref() уже не возвращает объект, что неверно: пока есть сильная ссылка, ref() возвращает объект.
- "False, затем False" — сочетание, которое не соответствует семантике слабых ссылок и поведению CPython в обычной ситуации.
Важные нюансы и исключения
- Поведение, описанное выше, характерно для CPython из‑за немедленного освобождения объектов при достижении нулевого счётчика ссылок. В других реализациях Python (PyPy, Jython, IronPython) сборка мусора может быть не детерминированной — объект может быть собран позже, поэтому результат после del obj может быть не предсказуемым без принудительного запуска сборщика.
- Если на объект есть другие сильные ссылки (например, список или кортеж, содержащие ссылку), удаление имени obj не освободит объект, и ref() продолжит возвращать объект.
- Если у класса A реализован метод
__del__или объект участвует в циклических ссылках, момент удаления может отличаться: сборщик мусора может отложить освобождение, пока разрулит финализацию или циклы. - Не все объекты поддерживают слабые ссылки (например, встроенные неизменяемые типы, такие как int или tuple, обычно не поддерживают weakref без явного допуска).
Демонстрация с явным запуском сборщика
Чтобы показать поведение, можно вызвать сборщик вручную (gc.collect) — это полезно в реализациях с отложенной сборкой:

В CPython gc.collect() обычно не нужен — объект уничтожается при del, но вызов полезен для демонстрации в других реализациях.
Выводы
- weakref.ref создаёт слабую ссылку: сама по себе она не удерживает объект живым.
- В CPython удаление последней сильной ссылки приводит к немедленному освобождению объекта, поэтому ref() возвращает None.
- При переносе кода между реализациями Python учитывайте недетерминированность сборки мусора и возможные отличия в моменте уничтожения объектов.