42

I am trying to use the .keys() and instead of getting a list of the keys like always have in the past. However I get this.

b = { 'video':0, 'music':23 }
k = b.keys()
print( k[0] )

>>>TypeError: 'dict_keys' object does not support indexing

print( k )
dict_keys(['music', 'video'])

it should just print ['music', 'video'] unless I'm going crazy.

What's going on?

gsamaras
  • 71,951
  • 46
  • 188
  • 305
tokageKitayama
  • 551
  • 1
  • 4
  • 5
  • 3
    Since nobody linked the official documentation yet, I'll do it: [**Dictionary view object**](http://docs.python.org/py3k/library/stdtypes.html#dictionary-view-objects). – Rik Poggi Jan 21 '12 at 14:30
  • Please note that dictionaries are unordered, so that b.keys()[0] is generally not a very useful thing to do. – Lennart Regebro Jan 21 '12 at 20:32

5 Answers5

91

Python 3 changed the behavior of dict.keys such that it now returns a dict_keys object, which is iterable but not indexable (it's like the old dict.iterkeys, which is gone now). You can get the Python 2 result back with an explicit call to list:

>>> b = { 'video':0, 'music':23 }
>>> k = list(b.keys())
>>> k
['music', 'video']

or just

>>> list(b)
['music', 'video']
Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • 16
    just `list(b)` does the same – Jochen Ritzel Jan 21 '12 at 14:19
  • I see thank you. So how would you go about iterating through the dict_keys? – tokageKitayama Jan 21 '12 at 14:21
  • 3
    @tokageKitayama: `for x in b.keys()` or just `for x in b`. – Fred Foo Jan 21 '12 at 14:23
  • Unless you're using the `set`-like behaviors of the keys view, just don't use `.keys()`. The dict itself supports iteration and contains checks of its keys with no calls; it's wasteful and unnecessarily verbose to actually call `.keys()` when you're no using it for something different from what the original `dict` does. – ShadowRanger Jan 01 '16 at 17:04
8

If you assigned k like so:

k = list(b.keys())

your code will work.

As the error says, the dict_keys type does not support indexing.

NPE
  • 486,780
  • 108
  • 951
  • 1,012
2

This is one of the breaking changes between Python 2 and 3.

In Python 2:

>>> help(dict.keys)
keys(...)
    D.keys() -> list of D's keys

In Python 3:

>>> help(dict.keys)
keys(...)
    D.keys() -> a set-like object providing a view on D's keys

This change in behavior makes a lot of sense since a dict is semantically unordered and its keys are unique - just like a set.

This change means that you don't have to create a new list of keys every time you want to do some kind of set comparison with a dict's keys.

Getting the same behavior in 2 and 3

To help transition to Python 3, Python 2.7 has another dict method, viewkeys. The viewkeys method is most similar to Python 3's dict.keys method:

>>> d
{'a': None, 'c': None, 'b': None, 'd': None}
>>> for k in d.viewkeys(): print k
... 
a
c
b
d
>>> d.viewkeys() & set('abc')
set(['a', 'c', 'b'])

In Python 3, the closest analog to the old behavior is to pass dict.keys() to list:

>>> d
{'d': None, 'a': None, 'c': None, 'b': None}
>>> list(d.keys())
['d', 'a', 'c', 'b']

Or just pass the dict to list, since a dict will iterate over its keys anyways:

>>> list(d)
['d', 'a', 'c', 'b']

You could create a utility functions to abstract the behavior over 2 and 3:

if hasattr(dict, 'viewkeys'): # Python 2.7
    def keys(d):
        return d.viewkeys()
else:  # Python 3
    def keys(d):
        return d.keys()

And pass a dict to list to get the list form, and in both 2 and 3, you'll get the same output:

>>> d
{'b': None, 'a': None, 'c': None, 'd': None}
>>> keys(d)
dict_keys(['b', 'a', 'c', 'd'])
>>> list(d)
['b', 'a', 'c', 'd']
Russia Must Remove Putin
  • 374,368
  • 89
  • 403
  • 331
0

If you simply want a list of keys from a dictionary you can directly do like this:

b = {"name": "xyz", "class":"abc", "college": "qwert"}  
key_list = list(b)

key_list will contain all the key names as a list, though, this will not repeats a key, if found more than once. Duplicate keys will be counted as one.

vikas0713
  • 566
  • 7
  • 9
-2
import random
b = { 'video':0, 'music':23,"picture":12 }
random.choice(tuple(b.items()))

# Returns a random dictionary entry as a tuple:
# ('music', 23)
divibisan
  • 11,659
  • 11
  • 40
  • 58
  • You should write more details regarding the answer (entering text answer and explanation, why is something working that way) + use better formating for the code directly (to keep standarts for the SO). – Tatranskymedved Jan 18 '18 at 08:29