27

In Python 2.7, dictionaries have both an iterkeys method and a viewkeys method (and similar pairs for values and items), giving two different ways to lazily iterate over the keys of the dictionary. The viewkeys method provides the principal feature of iterkeys, with iter(d.viewkeys()) effectively equivalent to d.iterkeys(). Additionally, objects returned viewkeys have convenient set-like features. There thus are strong reasons to favor viewkeys over iterkeys.

What about the other direction? Apart from compatibility with earlier versions of Python, are there any ways in which iterkeys would be preferable to viewkeys? Would anything be lost by just always using viewkeys?

Michael J. Barber
  • 24,518
  • 9
  • 68
  • 88

4 Answers4

21

A dictionary view updates as the dictionary does, while an iterator does not necessarily do this.

This means if you work with the view, change the dictionary, then work with the view again, the view will have changed to reflect the dictionary's new state.

They provide a dynamic view on the dictionary’s entries, which means that when the dictionary changes, the view reflects these changes. Source

Example:

>>> test = {1: 2, 3: 4}
>>> a = test.iterkeys()
>>> b = test.viewkeys()
>>> del test[1]
>>> test[5] = 6
>>> list(a)
[3, 5]
>>> b
dict_keys([3, 5])

When changes are made to the size, an exception will be thrown:

>>> test = {1: 2, 3: 4}
>>> a = test.iterkeys()
>>> b = test.viewkeys()
>>> test[5] = 6
>>> list(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration
>>> b
dict_keys([1, 3, 5])

It's also worth noting you can only iterate over a keyiterator once:

>>> test = {1: 2, 3: 4}
>>> a = test.iterkeys()
>>> list(a)
[1, 3]
>>> list(a)
[]
>>> b = test.viewkeys()
>>> b
dict_keys([1, 3])
>>> b
dict_keys([1, 3])
Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
  • 2
    This is all quite true, but doesn't actually address the question - the OP knows what `viewkeys` does, and is specifically asking whether there are any disadvantages to pretending `iterkeys` doesn't exist. – lvc Apr 17 '12 at 11:05
  • 2
    @lvc It explains the differences, which could be considered a positive of ``iterkeys()`` - if, for example, you do not want the possibility it getting updated, using ``iterkeys()`` could be more suitable (although the same functionality could be created with ``viewkeys()``). I'm not saying it's a strong reason, but it's pretty much the only one. – Gareth Latty Apr 17 '12 at 11:09
  • @larsmans One could argue that it was better than silently working on the updated version where you didn't want it to, but I agree it's a stretch. Yes, the short answer is there is no reason to use ``iterkeys()``, which is why it's gone in Python3. However, I thought I'd explain the differences in the hope it made the distinction clear. – Gareth Latty Apr 17 '12 at 11:16
  • @Lattyware I do appreciate your answer, although it seems not much of an advantage. I can't recall ever encountering the RuntimeError raised by altering a dictionary while iterating, or wanting to, for that matter. Any thoughts on an actual use case? – Michael J. Barber Apr 17 '12 at 11:26
  • @MichaelJ.Barber Pretty much never. It's very rare that you don't use an iterator as soon as you generate it. I'm simply talking possibility here, this is about all the difference you can get. As others have stated, the only real reason to ever use ``iterkeys()`` and friends is when you want backwards compatibility, other than that, dump them. – Gareth Latty Apr 17 '12 at 11:29
  • 3
    It is important to realise that the iterators do not raise a `RuntimeError` for any change in the dictionary; only for a size change. `del test[3]; test[5] = 6` between iterator creation and iteration will not faze it, nor will `test[3] = 91`. Thus it's dangerous. – Chris Morgan Apr 17 '12 at 12:04
  • @ChrisMorgan I honestly thought I'd already put in an example of that, updated, cheers. It makes my earlier point about not working on an updated version invalid, so disregard that. – Gareth Latty Apr 17 '12 at 12:09
  • 2
    CAUTION: doc page for 2.7 say "Iterating views while adding or deleting entries in the dictionary may raise a RuntimeError or fail to iterate over all entries." I read this to mean that it is safe to pass a dictview around, and START an iteration later (after making changes), but once you've started iterating, the dictionary cannot safely be changed. If one is not aware of this caveat, one might think that view methods give a way to modify a dictionary while iterating over it. Not safe (in 2.7). Instead, make a list of "keys to remove" while iterating, THEN remove those keys later – ToolmakerSteve Dec 10 '13 at 19:28
16

Functionality-wise, as you have observed, views are better. Compatibility-wise, they're worse.

Some performance metrics, taken from Python 2.7.2 on a 64-bit Ubuntu machine:

>>> from timeit import timeit

Dealing with an empty dictionary:

>>> emptydict = {}
>>> timeit(lambda: emptydict.viewkeys())
0.24384498596191406
>>> timeit(lambda: list(emptydict.viewkeys()))
0.4636681079864502
>>> timeit(lambda: emptydict.iterkeys())
0.23939013481140137
>>> timeit(lambda: list(emptydict.iterkeys()))
1.0098130702972412

Constructing the view is slightly more expensive, but consuming the view is significantly faster than the iterator (a bit over twice as fast).

Dealing with a thousand-element dictionary:

>>> fulldict = {i: i for i in xrange(1000)}
>>> timeit(lambda: fulldict.viewkeys())
0.24295306205749512
>>> timeit(lambda: list(fulldict.viewkeys()))
13.447425842285156
>>> timeit(lambda: fulldict.iterkeys())
0.23759889602661133
>>> timeit(lambda: list(fulldict.iterkeys()))
15.45390510559082

Same results, though less marked; constructing the view is very slightly more expensive, but consuming it is quite definitely faster (15% faster).

For fair comparison with list(dict.viewkeys()) and list(dict.iterkeys()), dict.keys() is distinctlyfaster:

>>> timeit(lambda: emptydict.keys())
0.2385849952697754
>>> timeit(lambda: fulldict.keys())
7.842105150222778

Summary: it's a trade-off; better functionality (which you will rarely use) and performance (which will only very rarely be significant enough to worry you—if you're caring about such performance matters, you're probably already in the region of needing to work with numpy/scipy) versus better compatibility and muscle memory usage.

Personally, unless already depending on 2.7-only functionality or unless I am absolutely controlling the runtime environment, I would avoid dictionary views in Python 2 code. Even in these cases, my fingers still want to type iter instead of view, so I let 'em!

Chris Morgan
  • 86,207
  • 24
  • 208
  • 215
  • Some of us live ahead of the curve, I know I default just to ``items()`` and friends due to using Python 3.x a lot. I have to be careful when I go back to Python 2.x. To be honest, while 3.x support is a mixed bag, 2.7.3 support is pretty much universal at this point, give or take the odd one. – Gareth Latty Apr 17 '12 at 11:23
  • @Lattyware: I haven't had that issue because I haven't used Python 3 for any project yet (still depending on Python 2-only things, e.g. Django). – Chris Morgan Apr 17 '12 at 11:24
  • We are all constrained by the stuff we use. I head Django is getting pretty close to full 3.x compatibility though. Oh, and I forgot to say, +1 for the thorough speed analysis. – Gareth Latty Apr 17 '12 at 11:26
  • @Lattyware: yep, their investigations as part of Django 1.5 seem to be going well. Looking forward to being able to use Python 3 generally (though naturally there will still be other drags around). – Chris Morgan Apr 17 '12 at 11:28
  • My fingers agree with yours in wanting to type `iter` - that's actually what prompted my question! – Michael J. Barber Apr 17 '12 at 11:37
12

No, there's no advantage to iterkeys over viewkeys, in the same way that there's no advantage to keys over either of them. iterkeys is only around for back compatibility. Indeed, in Python 3, viewkeys is the only behaviour that still exists, and it has been renamed to keys - the viewkeys method is actually a backport of the Python 3 behaviour.

lvc
  • 34,233
  • 10
  • 73
  • 98
  • 5
    This does not explain any of the differences – SystemParadox May 16 '13 at 20:44
  • 7
    @SystemParadox the question explicitly doesn't ask exactly what the differences are - OP asks specifically if there is any advantage to using `iter*` over `view*`, and *already lists* the advantages in the other direction. – lvc May 20 '13 at 15:04
9

As the name (and documentation) indicate, the viewkeys(), viewvalues() and viewitems() methods return a view of the current elements in the dictionary meaning that if the dictionary changes, so does the view; views are lazy. In the general case keys views are set-like, and item views are only set-like if the values are hashable.

In what cases would be better to use the standard methods keys(), values() and items() ? You mentioned a very important one: backwards compatibility. Also, when you need to have a simple list of all keys, values or items (not a set-like, not an iterator), when you need to modify the returned list without modifying the original dictionary and when you need a snapshot of a dictionary's keys, values or items at a moment in time, independent of any posterior modifications over the dictionary.

And what about iterkeys(), itervalues() and iteritems()? They're a suitable alternative when you need a one-shot, constant-space, lazy iterator snapshot of a dictionary's contents that will tell you if the dictionary got modified while iterating (via a RuntimeError), also they're very important for backwards compatibility.

Óscar López
  • 232,561
  • 37
  • 312
  • 386