После создания представления ключей словаря исходный словарь изменили. Что увидит представление при последующем обходе?
Представление ключей словаря является динамическим объектом: при последующем обходе оно увидит актуальное содержимое исходного словаря, включая добавленные и удалённые ключи. Это не копия ключей, созданная в момент вызова keys().
Объект dict_keys отражает состояние словаря, с которым он связан. Само представление не является итератором: его можно обходить повторно, пока словарь не изменяется во время текущего обхода.
В Python 2 методы словаря вроде keys() обычно возвращали материализованный список. Такой подход был простым, но требовал дополнительной памяти и немедленно создавал копию всех ключей.
В Python 3 методы keys(), values() и items() возвращают специальные представления. Они позволяют работать с содержимым словаря без обязательного создания копии и сохраняют связь с исходным словарём.
Если разработчик считает результат keys() снимком состояния, он может получить неожиданное содержимое при отложенном обходе. Между созданием представления и его использованием словарь может измениться, например из-за обработчика событий или другого участка кода.
Обратная ошибка тоже опасна: если нужен стабильный набор ключей, динамическое представление не гарантирует, что данные останутся неизменными. Кроме того, изменение размера словаря во время активного обхода обычно приводит к RuntimeError.
Вызов keys() создаёт объект-представление, хранящий связь с исходным словарём, а не отдельный список ключей. Когда начинается обход, представление получает элементы из текущего состояния словаря.
Представление поддерживает повторный обход: новый цикл снова обращается к словарю и может увидеть уже обновлённые ключи. Поэтому оно отличается и от списка-снимка, и от одноразового итератора.
Если нужен снимок, его создают явно — например, преобразуют представление в список или кортеж. Это требует памяти O(n), зато последующие изменения словаря не повлияют на сохранённые ключи.
Изменять размер словаря во время обхода его представления нельзя безопасно. Для преобразования или удаления элементов обычно сначала получают отдельный список кандидатов, завершают обход, а затем изменяют словарь.
Представления также поддерживают операции над множествами там, где это применимо: например, можно сравнивать набор ключей с другим множеством. Однако это не превращает представление в независимую копию — связь с исходным словарём сохраняется.
Сервис формирует отчёт по зарегистрированным пользователям. Он получает представление ключей реестра, после чего обработчик одного пользователя может зарегистрировать нового пользователя до фактического формирования отчёта.
Вариант с непосредственным хранением представления экономит память и автоматически отражает актуальное состояние, но результат отчёта становится зависимым от момента обхода. При изменении словаря во время обхода можно получить исключение.
Вариант с копированием в список создаёт стабильный снимок и предсказуемый отчёт, но расходует дополнительную память и не показывает пользователей, зарегистрированных после создания снимка.
Для отчёта выбран список ключей, созданный непосредственно перед началом обработки. Это явно фиксирует границу данных: отчёт строится по состоянию реестра на момент запуска, а изменения после этого попадут уже в следующий отчёт.
Нет. Представление можно обходить многократно, потому что каждый новый цикл получает итератор для текущего состояния словаря. Само представление не обязано реализовывать метод __next__; одноразовым объектом обхода является возвращённый итератор.
Представление ключей не содержит значений, поэтому изменение значения не меняет набор элементов, который оно предоставляет. Представление значений при последующем обходе увидит новое значение, поскольку также связано с исходным словарём.
Нельзя надёжно удалять элементы из словаря во время обхода его представления: изменение размера словаря нарушает текущую итерацию. Сначала формируют отдельный список ключей, подходящих под условие, затем обходят этот список и удаляют соответствующие записи из исходного словаря.