Доступ к элементу dict_keys по индексу в Python3


131

Я пытаюсь получить доступ к элементу dict_key по его индексу:

test = {'foo': 'bar', 'hello': 'world'}
keys = test.keys()  # dict_keys object

keys.index(0)
AttributeError: 'dict_keys' object has no attribute 'index'

Я хочу получить foo.

то же самое с:

keys[0]
TypeError: 'dict_keys' object does not support indexing

Как я могу это сделать?


9
В py3.x dict.keys()возвращает набор, подобный объекту представления, а не списку (поэтому индексация невозможна). Использованиеkeys = list(test)
Ашвини Чаудхари

когда я делаю list (something_dict), порядок кортежей является случайным: например: list ({'foo': 'bar', 'hello': 'world'}) может вернуть: list (foo, hello) или list (hello, foo), почему это случайно?
fj123x

7
В словах нет никакого порядка (используйте, collections.OrderedDictесли хотите заказать ключи)
Ашвини Чаудхари,

Интересное обсуждение безопасности потоков здесь, где рассматриваются различные подходы к решению этой проблемы: blog.labix.org/2008/06/27/…
Пол

Ответы:


161

list()Вместо этого вызовите словарь:

keys = list(test)

В Python 3 dict.keys()метод возвращает объект представления словаря , который действует как набор. Итерация по словарю напрямую также дает ключи, поэтому превращение словаря в список приводит к списку всех ключей:

>>> test = {'foo': 'bar', 'hello': 'world'}
>>> list(test)
['foo', 'hello']
>>> list(test)[0]
'foo'

3
Следите за списком (dict.keys ()) в Python 3 - в блоге Labix четко объясняется, в чем проблема, без предоставления решения. Это отлично!
Брэндон Брэдли

@BrandonBradley: вообще говоря: полагаться на то, что определенные действия являются атомарными, в любом случае - плохая идея, поскольку Python очень динамичен. Ваш код может быть легко передан в dictподкласс, например, где .keys()он обрабатывается в коде Python (например, может иметь место переключение потока).
Martijn Pieters

@BrandonBradley: спасибо за ссылку. В Python3 у меня работает только решение из одного из комментариев к этому блогу: sorted (dict.keys ()). В Python2 dict.keys () вернет список значений ключей.
Good Will

2
@GoodWill: sorted(dict)сделал бы то же самое; создать список ключей в отсортированном порядке. list(dict)предоставит вам список в порядке словаря.
Мартейн Питерс

1
или можно использоватьkeys = [*test]
Alex78191

59

Не полный ответ, но, возможно, полезный намек. Если это действительно первая вещь, которую вы хотите *, тогда

next(iter(q))

намного быстрее, чем

list(q)[0]

для больших dicts, так как все это не должно храниться в памяти.

Я обнаружил, что для 10.000.000 элементов это почти в 40.000 раз быстрее.

* Первый элемент в случае, если dict был просто псевдослучайным элементом до Python 3.6 (после этого он заказывается в стандартной реализации, хотя не рекомендуется полагаться на него).


5
Это всегда было одной из моих самых больших проблем, когда все становилось итераторами в Py3. Это определенно более эффективно, но так много вариантов использования становятся «нечистыми» и сложными без всякой причины. Например, почему они не могут просто поддерживать индексацию на итераторе без потери производительности?
Ehsan Kia

2

Мне нужна пара «ключ» и «значение» для первого элемента словаря. Я использовал следующий код.

 key, val = next(iter(my_dict.items()))

0
test = {'foo': 'bar', 'hello': 'world'}
ls = []
for key in test.keys():
    ls.append(key)
print(ls[0])

Обычный способ добавления ключей к статически определенному списку и последующего индексирования его для того же


2
Это не так, но почему бы и нет ls = list(test.keys())? Я считаю, что это проще.
Валентино

Да, это более эффективно. Я написал это просто для удобства чтения.
пранав дуа

0

Во многих случаях это может быть проблема XY . Почему вы индексируете ключи словаря по позициям? Вам действительно нужно? До недавнего времени словари в Python даже не упорядочивались, поэтому доступ к первому элементу был произвольным.

Я только что перевел код Python 2 на Python 3:

keys = d.keys()
for (i, res) in enumerate(some_list):
    k = keys[i]
    # ...

что не очень красиво, но и не очень плохо. Сначала я собирался заменить его чудовищным

    k = next(itertools.islice(iter(keys), i, None))

прежде чем я понял, что все это намного лучше написано как

for (k, res) in zip(d.keys(), some_list):

который отлично работает.

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

Внимательно посмотрите на свой код, если вам действительно нужен доступ к disk_keysэлементу по индексу. Возможно, вам и не нужно.


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.