Обращение к элементу объекта dict_keys
, dict_values
или dict_items
по индексу вызывает ошибку типа. Например:
> my_dict = {"foo": 0, "bar": 1, "baz": 2}
> my_dict.items()[0]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
----> 1 my_dict.items()[0]
TypeError: 'dict_items' object is not subscriptable
Мой вопрос не в том, как с этим справиться. Я знаю, что можно просто вызвать list()
или tuple()
для объекта dict_keys
, dict_values
или dict_items
, а затем подписать его.
Мой вопрос: почему такое поведение все еще сохраняется в Python, учитывая, что порядок элементов в словаре гарантирован начиная с Python 3.7.0. Почему невозможно (или нежелательно) провести рефакторинг типов dict_keys
, dict_values
и dict_items
, чтобы они могли подписываться?
В документации к stdlib эти типы описаны как «объекты просмотра». Я полагаю, что именно здесь находится ответ на мой вопрос, но я не могу найти более подробное описание того, что на самом деле представляет собой «объект представления». Может ли кто-нибудь просветить меня по этому вопросу?
Метод items
возвращает представление, отражающее текущее состояние словаря. Но словари — это не простые массивы, поэтому индексирование не имеет особого смысла. Подробную информацию см. https://docs.python.org/3.12/library/stdtypes.html#typesmapping.
@OldBoy К сожалению, это само по себе ничего не объясняет, и я думаю, что ОП уже зашел так далеко.
Если ты print(my_dict.items())
, то получишь dict_items([('foo', 0), ('bar', 1), ('baz', 2)])
. Это похоже на список кортежей, но я думаю, это не так. «dict_items» — это объект представления с некоторым внутренним представлением, а не реальным списком. Если вы хотите получить «n-й» предмет, лучшее, что я мог придумать, это next(itertools.islice(iter(my_dict), n, n+1))
.
Я думаю, что основная структура данных не является причиной. Например, deque — это связанный список, но он допускает индексацию, хотя это операция O(n).
Я думаю, это было просто дизайнерское решение. Несмотря на то, что сохранение порядка делает индексирование значимым, им не хотелось его добавлять. Я думаю, что они просто кодифицировали функцию реализации CPython, и у нее не было индексации.
@Toyota Кажется, вы добавили неправильный тег, поэтому я исправил его. Просто сообщаю вам
Я не знал о dictview. Благодарить.
Стоит отметить, что сами дикты не имеют метода выбора по индексу. Сравните это с объектами Pandas, у которых есть .iloc для выбора по позиции, а __getitem__
или .loc
для выбора по метке.
@ken: Предполагаемые варианты использования разные. Просмотр первого или последнего элемента двухсторонней очереди чрезвычайно распространен. Доступ, скажем, ко второму или третьему элементу встречается гораздо реже, но все же гораздо чаще встречается в случаях использования, для которых предназначены деки, чем доступ по индексу в случаях использования, для которых предназначены dicts. Индексирование обеспечивает уровень удобства для этих операций, который, по мнению участвующих в нем людей, того стоил. (Кроме того, связанный список, лежащий в основе дека, развернут, что значительно ускоряет индексацию по сравнению с итерацией.)
@user2357112 user2357112 Я хотел сказать, что если считается, что он приносит достаточную пользу, то «это не подходящий класс контейнера для него» не может быть причиной. Я не сравниваю его с deque буквально. Однако я бы не сказал, что индексирование представлений dict дает достаточную пользу.
«Упорядоченный» не означает «эффективно индексируемый». Нет более эффективного способа получить N-й элемент представления dict, чем выполнить итерацию N шагов.
Разрешение индексации создаст впечатление, что представления могут эффективно поддерживать эту операцию, что, в свою очередь, приведет к крайне неэффективному кодированию. Попытка изменить реализацию так, чтобы индексирование было эффективным, сделает другие операции с диктовками намного менее эффективными, что нецелесообразно для случаев использования, для которых на самом деле предназначены дикты.
Нет более эффективного способа..." - Но он может быть. Так почему же это не сделано?
@nocomment: Это потребует значительного увеличения затрат на другие операции. Удаление O(n) dict — не та цена, которую стоит платить за это.
Это можно сделать за O(log n). Но да, я думаю, что это одна из причин, и было бы хорошо осветить ее в ответе.
Вы уверены в этом? Насколько я понимаю, таблицу записей можно просто проиндексировать.
@juanpa.arrivillaga: Удаление оставляет дыры, плюс API также должен иметь смысл для collections.OrderedDict
, который использует совершенно другую систему отслеживания заказов. У диктовок с разделенной таблицей также есть свои проблемы.
В очень специфическом подмножестве случаев можно было бы напрямую индексировать dk_entries
, но производительность была бы очень нестабильной. Изменения либо в реализации dict, либо в коде, основанном на индексировании, могут привести к резкому снижению производительности до O(n). Разработчики не хотят создавать интерфейсы, которые поощряют хрупкий код.
ах да. поэтому в таблице записей могут быть пустые маркеры, поэтому вам все равно придется пройти по таблице, чтобы получить n-й элемент
Ваша логика звучит разумно, но разве не было бы неправильно, если бы в списке было insert
?
@ken: Вставка, хотя и неэффективна, является слишком распространенной операцией, чтобы ее можно было разумно исключить из списков, а попытка воспроизвести эту функциональность без встроенной поддержки невероятно громоздка и еще более неэффективна. Напротив, доступ к входу по позиции крайне редко встречается в случаях использования, для которых предназначены представления dict, и для таких случаев использования легко доступны лучшие альтернативы.
@user2357112 user2357112 Если вы думаете, что разница в том, распространена она или нет, и что распространенность является достаточной причиной для реализации чего-то, даже если это неэффективно, тогда ваш ответ должен быть «потому что это не распространено». Эффективность — это всего лишь недостаток, а не причина. Вероятно, здесь скопилось много людей, хотя я могу полностью согласиться с вашим мнением, но единственный ответ, который мы можем дать, - это основанный на мнении ответ «от реализации нет никакой пользы».
@ken: Частота — это фактор, но вопрос об альтернативах более важен. Отсутствие позиционного доступа для представлений dict побуждает людей использовать лучшие альтернативы. Если не использовать вставку для списков, люди будут использовать худшие альтернативы.
И доступность альтернатив является важной частью частоты: люди пытались бы получить доступ к записям dict по положению гораздо чаще, если бы альтернативы использованию списков не было.
@ user2357112 То же самое можно сказать и с противоположной точки зрения: если вы исключите вставку для списков, люди могут использовать лучшую альтернативу, чем вставка, а если вы исключите позиционный доступ для представлений dict, люди могут использовать худшую альтернативу (фактически , предложенный ОП метод может быть одним из примеров).
Не уверен, что здесь есть удовлетворительный ответ. До того, как дикты были гарантированно упорядочены, доступ к их элементам по смещению не имел никакого практического смысла. Таким образом, дикты никогда не использовались как упорядоченные структуры данных. Просто потому, что сейчас ситуация изменилась, лично мне еще никогда не приходилось полагаться на порядок диктовок. Для пар упорядоченных значений существует старый добрый список кортежей. Так что, возможно, из-за этого никто никогда не пересматривал эту политику. Или могут быть веские аргументы против его реализации; SO не очень уместно раскапывать такие обсуждения.