177

I'm trying to access a dict_key's element by its index:

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

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

I want to get foo.

same with:

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

How can I do this?

jopasserat
  • 5,534
  • 4
  • 30
  • 49
fj123x
  • 5,888
  • 11
  • 42
  • 56
  • 13
    In py3.x `dict.keys()` returns a set like view object, not list(so, indexing is not possible). Use `keys = list(test)` – Ashwini Chaudhary Aug 31 '13 at 19:32
  • when i do list(something_dict), the tuple order is random: example: list({'foo': 'bar', 'hello': 'world'}) can returns: list(foo, hello) or list(hello, foo), why is this random? – fj123x Aug 31 '13 at 19:48
  • 7
    [Dicts don't have any order](http://stackoverflow.com/questions/526125/why-is-python-ordering-my-dictionary-like-so).(Use `collections.OrderedDict` if you want ordered keys) – Ashwini Chaudhary Aug 31 '13 at 19:49
  • Interesting discussion on thread safety here reagrding the various approaches to solve this problem: http://blog.labix.org/2008/06/27/watch-out-for-listdictkeys-in-python-3 – Paul May 10 '16 at 18:01

6 Answers6

248

Call list() on the dictionary instead:

keys = list(test)

In Python 3, the dict.keys() method returns a dictionary view object, which acts as a set. Iterating over the dictionary directly also yields keys, so turning a dictionary into a list results in a list of all the keys:

>>> test = {'foo': 'bar', 'hello': 'world'}
>>> list(test)
['foo', 'hello']
>>> list(test)[0]
'foo'
Martijn Pieters
  • 963,270
  • 265
  • 3,804
  • 3,187
  • 5
    [Watch out for list(dict.keys()) in Python 3 - Labix Blog](http://blog.labix.org/2008/06/27/watch-out-for-listdictkeys-in-python-3) clearly explains what the issue is without providing a solution. This is excellent! – Brandon Bradley Jun 10 '14 at 15:38
  • @BrandonBradley: generally speaking: relying on certain actions being atomic is a bad idea anyway, as Python is so highly dynamic. Your code can easily be passed a `dict` subclass, for example, where `.keys()` is handled in Python code (e.g. a thread switch can take place). – Martijn Pieters Jun 10 '14 at 15:41
  • @BrandonBradley: thanks for the link. Only the solution from one of the comments for that blog works for me in Python3: sorted(dict.keys()). In Python2, dict.keys() will return a list of key values. – Good Will May 08 '18 at 22:20
  • 2
    @GoodWill: `sorted(dict)` would do the exact same thing; produce a list of keys in sorted order. `list(dict)` will give you the list in dictionary order. – Martijn Pieters May 09 '18 at 16:11
  • 1
    or to can use `keys = [*test]` – Alex78191 Dec 13 '19 at 02:52
  • @Alex78191: that only became available in Python 3.5. I'm not convinced yet that for a single iterable-to-list conversion that that syntax is readable and clear enough. – Martijn Pieters Dec 13 '19 at 16:10
74

Not a full answer but perhaps a useful hint. If it is really the first item you want*, then

next(iter(q))

is much faster than

list(q)[0]

for large dicts, since the whole thing doesn't have to be stored in memory.

For 10.000.000 items I found it to be almost 40.000 times faster.

*The first item in case of a dict being just a pseudo-random item before Python 3.6 (after that it's ordered in the standard implementation, although it's not advised to rely on it).

Mark
  • 16,990
  • 6
  • 100
  • 122
  • 8
    This has always been one of my biggest issues with everything becoming iterators in Py3. It's definitely more efficient, but so many use cases become "unclean" and complicated for no reason. For example, why can't they just support indexing on the iterator, without necessarily having a performance loss? – Ehsan Kia Sep 05 '17 at 07:43
  • @EhsanKia how can one go about doing this. Iterators are used specifically so that indexing isn't required, you can't access a random element from the collection, and that's why is has performance benefits. – AAAlex123 Feb 26 '21 at 11:08
  • 1
    Right, I'm not saying it for performance reasons, just for usability. q[N] would just be syntactic sugar / shorthand for `list(q)[N]` behind the scene, or actually more like next() called N times on it, so it also works for infinite iterators. Right now as it is, what's the cleanest way to get the 5th index of an infinite iterator? I don't even think itertools has any solution for this. Best is `next(itertools.islice(q, 5, None))` – Ehsan Kia Feb 27 '21 at 19:35
  • @EhsanKia you've just written one solution based on itertools, and probably the best -- see the `nth()` recipe in itertools: https://docs.python.org/3/library/itertools.html#itertools-recipes. This is not, however, the issue. A syntactic sugar wouldn't be very useful (advisable?), because accessing a sequence (a list) by index is essentially different than accessing an iterable in any way -- the first can be repeated indefinitely but the second cannot, as it consumes the iterable. – petre Nov 03 '21 at 08:24
8

Python 3

mydict = {'a': 'one', 'b': 'two', 'c': 'three'}
mykeys = [*mydict]          #list of keys
myvals = [*mydict.values()] #list of values

print(mykeys)
print(myvals)

Output

['a', 'b', 'c']
['one', 'two', 'three']

Also see this detailed answer

woodz
  • 527
  • 4
  • 11
7

I wanted "key" & "value" pair of a first dictionary item. I used the following code.

 key, val = next(iter(my_dict.items()))
Sheikh Abdul Wahid
  • 2,260
  • 2
  • 20
  • 24
0
test = {'foo': 'bar', 'hello': 'world'}
ls = []
for key in test.keys():
    ls.append(key)
print(ls[0])

Conventional way of appending the keys to a statically defined list and then indexing it for same

pranav dua
  • 31
  • 2
0

In many cases, this may be an XY Problem. Why are you indexing your dictionary keys by position? Do you really need to? Until recently, dictionaries were not even ordered in Python, so accessing the first element was arbitrary.

I just translated some Python 2 code to Python 3:

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

which is not pretty, but not very bad either. At first, I was about to replace it by the monstrous

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

before I realised this is all much better written as

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

which works just fine.

I believe that in many other cases, indexing dictionary keys by position can be avoided. Although dictionaries are ordered in Python 3.7, relying on that is not pretty. The code above only works because the contents of some_list had been recently produced from the contents of d.

Have a hard look at your code if you really need to access a disk_keys element by index. Perhaps you don't need to.

gerrit
  • 20,664
  • 14
  • 84
  • 160