[Tutor] use of the newer dict types

Steven D'Aprano steve at pearwood.info
Fri Jul 26 04:16:09 CEST 2013


On 26/07/13 07:49, Jim Mooney wrote:
> If you do dict.keys() in 2.7 you get a list, which is quite handy. But
> if you do it in 3.3 you get this odd dict_keys type, for which I have
> yet to find a use, and which just requires an extra list step to use.
> The same for values. Since most changes from 2.7 to 3.3 were
> improvements, what am I missing here? What is the use of these types?


Oh wow, deja vu. The same question was asked on comp.lang.python a day or two ago.

Views are what Python dicts should have had 20 years ago, but didn't. Python 3 finally fixes that ancient design.

In Python 2, the preferred way to iterate over a dictionary's keys is:

for key in mydict: ...


There is no need to extract the keys into a list, then iterate over the list. That does not change in Python 3.

In Python 2, the preferred way to iterate over values or key/value pairs was to use an iterator, since that is more light-weight than a list:

for value in mydict.itervalues(): ...

is preferred over just mydict.values() since it doesn't make a full list copy of the values. For merely iterating over the items one at a time, it is wasteful and inefficient to build a list, and calling values() should be avoided. Likewise for iteritems() versus items(). On the rare occasions you actually need a list, simply call list() explicitly on the view.

In Python 3, you just iterate over mydict.values() or items(), which return views. For plain iterating, the only difference between the two is that iterators can only be run through once, while views can be run over again and again. If you really need a one-shot iterator, just call iter() on the view.

Views are similar to sets, and support some set operations, which is great for comparing elements of two dicts:

py> d1 = dict.fromkeys([1, 2, 3, 4])
py> d2 = dict.fromkeys([3, 4, 5, 6])
py> d1.keys() & d2.keys()  # keys that are in both
{3, 4}
py> d1.keys() ^ d2.keys()  # keys not in both
{1, 2, 5, 6}
py> d1.keys() - d2.keys()  # keys only in d1
{1, 2}
py> d2.keys() - d1.keys()  # keys only in d2
{5, 6}

In Python 2.7, use viewkeys(). In Python 2.6, you're out of luck, and need to program that functionality yourself.

Because views are set-like, they provide efficient, almost constant-time membership testing for keys and items. (But not values, for technical reasons.)

Views also provide read-only, live access to dict keys, items and values.

In both Python 2 and 3, it is safe to modify the value associated with a key, but not delete or add keys. Basically, it is unsafe to change the length of a container (dict, list, or set) while iterating over it. "Unsafe" means that doing so can lead you to unexpectedly skipping or repeating items. Dicts can detect some (but not all) changes, and explicitly raise an error. Consequently, iterating over a dict while deleting keys is one of the few cases where you actually do need to use a separate key list instead of a view. In that case, simply call

for key in list(mydict):  # or list(mydict.keys())
     ...


-- 
Steven


More information about the Tutor mailing list