2025, Nov 03 18:01
Стабильные визуализации memory_graph: show() vs render()
Почему в Python memory_graph два show() видны один PDF: просмотрщик переиспользует файл с тем же именем. Решение: уникальный outfile для show() или render().
Когда вы исследуете память Python с помощью memory_graph и ожидаете, что несколько PDF‑снимков появятся один за другим, два подряд вызова show() могут ввести в заблуждение. На практике многие видят, что «залипает» только первый показ, либо мелькает лишь финальное состояние. При этом запись изображений через render() ведет себя предсказуемо. Разберемся, почему так происходит и как сделать визуализации стабильными.
Проблема: воспроизводимый пример
Следующий пример формирует две последовательные визуализации в PDF‑просмотрщике. Список присваивается по ссылке, затем изменяется, а show() вызывается до и после изменения. Задумка — увидеть два разных состояния.
import memory_graph as memg
vals = [4, 3, 2]
mirror = vals
memg.show(locals())
mirror += [1]
memg.show(locals())
Вместо двух показов просмотрщик нередко останавливается на первом PDF‑файле или заменяет его настолько быстро, что заметно остается только последнее состояние.
Почему render() выглядит безупречно
Запись статичных изображений с явными именами файлов работает стабильно, потому что выходные файлы различаются и не переиспользуются просмотрщиком.
import memory_graph as memg
nums = [4, 3, 2]
ref = nums
memg.render(locals(), 'snapshot1.png')
ref += [1]
memg.render(locals(), 'snapshot2.png')
Создаются два отдельных файла, и ничего не нужно обновлять «на месте» в самом просмотрщике.
Что на самом деле происходит
В Linux Mint обычно показывается только последний файл. Первый снимок открывается, но следующий вызов переиспользует то же окно и подменяет содержимое. Переключение настолько быстрое, что исходное состояние фактически не видно. Согласно исходному коду, show() внутри опирается на render() и генерирует файлы с одинаковыми именами, поэтому внешний просмотрщик просто обновляет уже открытое, а не запускает второе окно. Это согласуется с другим наблюдением: когда PDF‑просмотрщик блокирует открытый файл, обновления из второго вызова не могут заменить содержимое, из‑за чего отображение кажется «застрявшим» на первом PDF. В последующих тестах Adobe Acrobat Reader демонстрировал такое блокирование, тогда как Sumatra PDF позволял обновления, как и ожидалось.
Решение: дайте каждому show() уникальный outfile
Чтобы видеть оба состояния рядом или в отдельных окнах, передавайте каждому вызову разное имя выходного файла.
import memory_graph as memg
items = [4, 3, 2]
shared = items
memg.show(locals(), outfile="view_1.pdf")
shared += [1]
memg.show(locals(), outfile="view_2.pdf")
С разными именами PDF вы получите два отображения. Это подтверждает наблюдение: добавление outfile="file.pdf" с разными именами показывает оба файла и указывает, что решающий фактор — внешний просмотрщик.
Почему это важно
При изучении алиасинга объектов, изменяемости и графов ссылок часто нужно фиксировать последовательность состояний. Инструменты не должны искажать картину. Понимание того, что просмотрщик может переиспользовать или блокировать один и тот же файл, помогает избежать ложных выводов о состоянии памяти. Выбор просмотрщика, который не блокирует обновления, или выпуск файлов с уникальными именами обеспечивает надежную диагностику.
Практические выводы
Если вы используете show(), указывайте уникальный outfile для каждого снимка — так получите несколько окон или последовательные файлы, которые не перезаписывают друг друга. Если ваш просмотрщик не позволяет обновлять открытый PDF, перейдите на тот, который поддерживает «живое» обновление; в тестах Sumatra PDF работал там, где Adobe Acrobat Reader блокировал изменения. Если нужны артефакты для последующего анализа, рендерьте через render() в уникальные имена изображений — это прямой и удобный путь. А если сомневаетесь, как show() формирует выходные файлы, загляните в исходники проекта: там видно, что show() построен поверх render(), что объясняет поведение с одинаковыми именами и последующее обновление в просмотрщике.
Коротко: ваш код корректен, виновато взаимодействие с внешним PDF‑просмотрщиком. Используйте уникальные имена файлов или просмотрщик, который не блокирует файл, — и графы памяти будут отображаться именно так, как задумано.
Статья основана на вопросе на StackOverflow от user31005948 и ответе от furas.