Weak references в Python: что выведет код?

Weak references в Python: что выведет код?

@python_quiz

Разберем этот квиз

Короткий фрагмент кода:

python

Что здесь происходит и почему вывод будет именно таким.

Краткое объяснение поведения

  • 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) — это полезно в реализациях с отложенной сборкой:

python

В CPython gc.collect() обычно не нужен — объект уничтожается при del, но вызов полезен для демонстрации в других реализациях.

Выводы

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

Report Page