python 3 dict: .keys(), .values(), and .item()
Steve D'Aprano
steve+python at pearwood.info
Sat Jan 7 19:48:56 EST 2017
On Sun, 8 Jan 2017 08:48 am, Ethan Furman wrote:
> In Python 2 we have:
>
> dict().keys() \
> dict().items() --> separate list() of the results
> dict().values() /
>
> and
>
> dict().iter_keys() \
> dict().iter_items() --> integrated iter() of the results
> dict().iter_values() /
You missed another group of methods:
viewkeys()
viewitems()
viewvalues()
which are integrated, iterable, set-like views of the keys/items/values.
> By "separate list" I mean a snapshot of the dict at the time, and by
> "integrated iter()" I mean changes to the dict during iteration are
> seen by the iter.
>
> In Python 3 the iter_* methods replaced the list() type methods,
Its actually the view* methods.
> which
> makes sense from the point-of-view of moving to a more iterator based
> language; however, as a result of that change the typical "iterate over
> a dict" operation now has a built-in gotcha: modifying the dict during
> the iteration can now cause exceptions.
Even in Python 2, modifying the dict during iteration can cause exceptions:
for key in adict: # iterate over the dict directly
...
As usual, the standard rule applies: don't add or remove elements of a
collection while you are iterating over it.
This doesn't work either:
for i, x in enumerate(alist):
if condition(x):
del alist(i)
and has a similar solution:
for i, x in enumerate(list(alist)):
...
except that's more commonly written using slicing:
for i, x in enumerate(alist[:]):
...
For both dicts and lists, it is safe to modify existing elements:
adict[key] = new_value # provided key already exists
alist[index] = new_value
are both safe, but insertions and deletions are not.
> The solution, of course, is simple: surround the iterator call with
> list():
There's nothing magical about list. You could use tuple, or (sometimes) set
or frozenset. But list is good.
> list(dict.keys())
> list(dict.items())
> list(dict.values())
>
> for k, v in list(flag._value2member_map_.items()):
> ...
>
> The solution, however, feels a lot more boilerplate-ish. Either the
> programmer takes a lot more care to remember the current state of the dict
> (since it's no longer a snapshot), or "list()" is sprinkled around every
> iterator access.
Not *every* view access. Only the ones where you insert/delete elements.
> In other words, what used to be a completely safe operation now is not.
>
> Thoughts?
The old Python 2 keys/values/items methods used to make a copy of the
elements, whether you needed a copy or not. Now making a copy is your
responsibility. That feels more Pythonic to me.
--
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.
More information about the Python-list
mailing list