Почему dict_keys, dict_values ​​и dict_items не подлежат подписке?

Обращение к элементу объекта 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 эти типы описаны как «объекты просмотра». Я полагаю, что именно здесь находится ответ на мой вопрос, но я не могу найти более подробное описание того, что на самом деле представляет собой «объект представления». Может ли кто-нибудь просветить меня по этому вопросу?

Не уверен, что здесь есть удовлетворительный ответ. До того, как дикты были гарантированно упорядочены, доступ к их элементам по смещению не имел никакого практического смысла. Таким образом, дикты никогда не использовались как упорядоченные структуры данных. Просто потому, что сейчас ситуация изменилась, лично мне еще никогда не приходилось полагаться на порядок диктовок. Для пар упорядоченных значений существует старый добрый список кортежей. Так что, возможно, из-за этого никто никогда не пересматривал эту политику. Или могут быть веские аргументы против его реализации; SO не очень уместно раскапывать такие обсуждения.

deceze 22.08.2024 13:50

Метод items возвращает представление, отражающее текущее состояние словаря. Но словари — это не простые массивы, поэтому индексирование не имеет особого смысла. Подробную информацию см. https://docs.python.org/3.12/library/stdtypes.html#typesmapp‌​ing.

OldBoy 22.08.2024 13:54

@OldBoy К сожалению, это само по себе ничего не объясняет, и я думаю, что ОП уже зашел так далеко.

deceze 22.08.2024 13:56

Если ты print(my_dict.items()), то получишь dict_items([('foo', 0), ('bar', 1), ('baz', 2)]). Это похоже на список кортежей, но я думаю, это не так. «dict_items» — это объект представления с некоторым внутренним представлением, а не реальным списком. Если вы хотите получить «n-й» предмет, лучшее, что я мог придумать, это next(itertools.islice(iter(my_dict), n, n+1)).

Steven Dickinson 22.08.2024 17:01

Я думаю, что основная структура данных не является причиной. Например, deque — это связанный список, но он допускает индексацию, хотя это операция O(n).

ken 22.08.2024 17:12

Я думаю, это было просто дизайнерское решение. Несмотря на то, что сохранение порядка делает индексирование значимым, им не хотелось его добавлять. Я думаю, что они просто кодифицировали функцию реализации CPython, и у нее не было индексации.

Barmar 22.08.2024 17:58

@Toyota Кажется, вы добавили неправильный тег, поэтому я исправил его. Просто сообщаю вам

wjandrea 22.08.2024 20:45

Я не знал о dictview. Благодарить.

toyota Supra 22.08.2024 20:46

Стоит отметить, что сами дикты не имеют метода выбора по индексу. Сравните это с объектами Pandas, у которых есть .iloc для выбора по позиции, а __getitem__ или .loc для выбора по метке.

wjandrea 22.08.2024 20:58

@ken: Предполагаемые варианты использования разные. Просмотр первого или последнего элемента двухсторонней очереди чрезвычайно распространен. Доступ, скажем, ко второму или третьему элементу встречается гораздо реже, но все же гораздо чаще встречается в случаях использования, для которых предназначены деки, чем доступ по индексу в случаях использования, для которых предназначены dicts. Индексирование обеспечивает уровень удобства для этих операций, который, по мнению участвующих в нем людей, того стоил. (Кроме того, связанный список, лежащий в основе дека, развернут, что значительно ускоряет индексацию по сравнению с итерацией.)

user2357112 22.08.2024 21:44

@user2357112 user2357112 Я хотел сказать, что если считается, что он приносит достаточную пользу, то «это не подходящий класс контейнера для него» не может быть причиной. Я не сравниваю его с deque буквально. Однако я бы не сказал, что индексирование представлений dict дает достаточную пользу.

ken 23.08.2024 05:59
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
5
11
135
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

«Упорядоченный» не означает «эффективно индексируемый». Нет более эффективного способа получить N-й элемент представления dict, чем выполнить итерацию N шагов.

Разрешение индексации создаст впечатление, что представления могут эффективно поддерживать эту операцию, что, в свою очередь, приведет к крайне неэффективному кодированию. Попытка изменить реализацию так, чтобы индексирование было эффективным, сделает другие операции с диктовками намного менее эффективными, что нецелесообразно для случаев использования, для которых на самом деле предназначены дикты.

Нет более эффективного способа..." - Но он может быть. Так почему же это не сделано?

no comment 22.08.2024 21:25

@nocomment: Это потребует значительного увеличения затрат на другие операции. Удаление O(n) dict — не та цена, которую стоит платить за это.

user2357112 22.08.2024 21:26

Это можно сделать за O(log n). Но да, я думаю, что это одна из причин, и было бы хорошо осветить ее в ответе.

no comment 22.08.2024 21:29

Вы уверены в этом? Насколько я понимаю, таблицу записей можно просто проиндексировать.

juanpa.arrivillaga 22.08.2024 22:13

@juanpa.arrivillaga: Удаление оставляет дыры, плюс API также должен иметь смысл для collections.OrderedDict, который использует совершенно другую систему отслеживания заказов. У диктовок с разделенной таблицей также есть свои проблемы.

user2357112 22.08.2024 22:19

В очень специфическом подмножестве случаев можно было бы напрямую индексировать dk_entries, но производительность была бы очень нестабильной. Изменения либо в реализации dict, либо в коде, основанном на индексировании, могут привести к резкому снижению производительности до O(n). Разработчики не хотят создавать интерфейсы, которые поощряют хрупкий код.

user2357112 22.08.2024 22:27

ах да. поэтому в таблице записей могут быть пустые маркеры, поэтому вам все равно придется пройти по таблице, чтобы получить n-й элемент

juanpa.arrivillaga 22.08.2024 22:29

Ваша логика звучит разумно, но разве не было бы неправильно, если бы в списке было insert?

ken 23.08.2024 05:44

@ken: Вставка, хотя и неэффективна, является слишком распространенной операцией, чтобы ее можно было разумно исключить из списков, а попытка воспроизвести эту функциональность без встроенной поддержки невероятно громоздка и еще более неэффективна. Напротив, доступ к входу по позиции крайне редко встречается в случаях использования, для которых предназначены представления dict, и для таких случаев использования легко доступны лучшие альтернативы.

user2357112 23.08.2024 07:15

@user2357112 user2357112 Если вы думаете, что разница в том, распространена она или нет, и что распространенность является достаточной причиной для реализации чего-то, даже если это неэффективно, тогда ваш ответ должен быть «потому что это не распространено». Эффективность — это всего лишь недостаток, а не причина. Вероятно, здесь скопилось много людей, хотя я могу полностью согласиться с вашим мнением, но единственный ответ, который мы можем дать, - это основанный на мнении ответ «от реализации нет никакой пользы».

ken 23.08.2024 07:45

@ken: Частота — это фактор, но вопрос об альтернативах более важен. Отсутствие позиционного доступа для представлений dict побуждает людей использовать лучшие альтернативы. Если не использовать вставку для списков, люди будут использовать худшие альтернативы.

user2357112 23.08.2024 08:04

И доступность альтернатив является важной частью частоты: люди пытались бы получить доступ к записям dict по положению гораздо чаще, если бы альтернативы использованию списков не было.

user2357112 23.08.2024 08:13

@ user2357112 То же самое можно сказать и с противоположной точки зрения: если вы исключите вставку для списков, люди могут использовать лучшую альтернативу, чем вставка, а если вы исключите позиционный доступ для представлений dict, люди могут использовать худшую альтернативу (фактически , предложенный ОП метод может быть одним из примеров).

ken 23.08.2024 08:34

Другие вопросы по теме