[PATCH 09/15] Replace dictionary iteration methods
Nicolas Sebrecht
nicolas.s-dev at laposte.net
Fri May 13 12:32:04 BST 2016
On Fri, May 13, 2016 at 08:58:11AM +0200, Łukasz Żarnowiecki wrote:
> Here is a good explanation[1]. We can safely remove wrapping with list
> call when looping if you want to.
>
> > > @@ -1009,14 +1009,14 @@ class BaseFolder(object):
> > > delflaglist[flag] = []
> > > delflaglist[flag].append(uid)
> > >
> > > - for flag, uids in addflaglist.items():
> > > + for flag, uids in list(addflaglist.items()):
> >
> > Hmm, does this work as expected?
>
> Yes, output is almost the same. Underling hashing algorithm was
> probably changed and the data is differently ordered in memory.
>
> Python2
> -------
> >>> addflaglist = a
>
> >>> for flag, uids in list(addflaglist.items()):
> ... print(flag)
> ... print(uids)
> ...
> a
> 1
> c
> 3
> b
> 2
> >>> for flag, uids in addflaglist.items():
> ... print(flag)
> ... print(uids)
> ...
> a
> 1
> c
> 3
> b
> 2
>
> Python3
> -------
> addflaglist = {'a': 1, 'b': 2, 'c': 3}
> >>> for flag, uids in addflaglist.items():
> ... print(flag)
> ... print(uids)
> ...
> c
> 3
> b
> 2
> a
> 1
> >>> for flag, uids in list(addflaglist.items()):
> ... print(flag)
> ... print(uids)
> ...
> c
> 3
> b
> 2
> a
> 1
>
>
> [1] http://stackoverflow.com/questions/17695456/why-python-3-needs-wrap-dict-items-with-list
Thank you much for this pointer.
In response to the first comment there is:
Note: in general, it is not safe to ignore the warning if the loop
modifies the dict. It breaks on Python 3 without list(). – J.F.
Sebastian Jul 17 '13 at 18:28
This should never happen: a loop normally cannot modify the dictionary
it is iterating over; this generally raises a RuntimeError: dictionary
changed size during iteration (both in Python 2 and Python 3).
Reference:
docs.python.org/dev/whatsnew/2.7.html#pep-3106-dictionary-views – EOL
Jul 18 '13 at 2:45
d.items() returns a list on Python 2 therefore the loop can modify the
dictionary. Just try it: d = {1:2}; for k, v in d.items(): d[3]=4 -
this code works on Python 2 but it breaks on Python 3 with the error
that you've mentioned. – J.F. Sebastian Jul 18 '13 at 12:15
Indeed. I had the likes of my_dict.iteritems() in mind (which does
raise the exception I mentioned, even in Python 2): in the case you
cite, one is not "iterating over the dictionary" but over a list, as
you say. That said, now I see what you meant: that list() is necessary
in Python 3 if one wants to modify the dictionary. I had missed this
because (1) the warning is actually a little strange, as the for loop
statement is perfect Python 3 code; and (2) this is a relatively rare
situation (never modified a dictionary after items() in 7 years!). Good
point. :) – EOL Jul 18 '13 at 13:22
I wonder the real error is to actually modify a dict when looping over
it. So, instead of changing all the loops I would only fix the loops
actually requiring the list() wrapper and add a comment on top of it:
# Wrapping loop with list() because the dict is changed in this loop.
# This is required for Python 3.
This way, further contributors are aware of why the list() wrapper when
usefull.
--
Nicolas Sebrecht
More information about the OfflineIMAP-project
mailing list