Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > Re: dictionary size changed during iteration

Reply
Thread Tools

Re: dictionary size changed during iteration

 
 
Peter Otten
Guest
Posts: n/a
 
      04-20-2011
Laszlo Nagy wrote:

> Given this iterator:
>
> class SomeIterableObject(object):
> ....
> ....
>
> def __iter__(self):
> ukeys = self.updates.keys()
> for key in ukeys:
> if self.updates.has_key(key):
> yield self.updates[key]
> for rec in self.inserts:
> yield rec
> ....
> ....
>
> How can I get this exception:
>
> RuntimeError: dictionary changed size during iteration
>
>
> It is true that self.updates is being changed during the iteration. But
> I have created the "ukeys" variable solely to prevent this kind of
> error. Here is a proof of correctness:
>
>>>> d = {1:1,2:2}
>>>> k = d.keys()
>>>> del d[1]
>>>> k

> [1, 2]
>>>> k is d.keys()

> False
>
> So what is wrong with this iterator? Why am I getting this error message?


The keys() method which used to return a list in 2.x was changed in 3.x to
return a view object and to become more or less the equivalent of the old
dict.iterkeys():

>>> d = dict(a=1)
>>> keys = d.keys()
>>> keys

dict_keys(['a'])
>>> for k in keys:

.... d["b"] = 42
....
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration
>>> keys

dict_keys(['a', 'b'])

You now have to create the list explicitly to avoid the error:

>>> d = dict(a=1)
>>> keys = list(d.keys())
>>> for k in keys:

.... d["b"] = 42
....
>>> d

{'a': 1, 'b': 42}
>>> keys

['a']


 
Reply With Quote
 
 
 
 
Roy Smith
Guest
Posts: n/a
 
      04-22-2011
In article <mailman.644.1303306435.9059.python->,
Peter Otten <__peter__@web.de> wrote:

> You now have to create the list explicitly to avoid the error:
>
> >>> d = dict(a=1)
> >>> keys = list(d.keys())
> >>> for k in keys:

> ... d["b"] = 42
> ...


That works, but if d is large, it won't be very efficient because it has
to generate a large list.

If d is large, and the number of keys to be mutated is relatively small,
a better solution may be to do it in two passes. The first loop
traverses the iterator and builds a list of things to be changed. The
second loop changes them.

changes = [ ]
for key in d.iterkeys():
if is_bad(key):
changes.append(key)
for key in changes:
d[key] = "I'm not dead yet"

Both solutions are O(n), but the second may run significantly faster and
use less memory.
 
Reply With Quote
 
 
 
 
Laszlo Nagy
Guest
Posts: n/a
 
      05-06-2011

>> ...

> That works, but if d is large, it won't be very efficient because it has
> to generate a large list.

It is not large. But I'm using Python 2.6 , not Python 3.

I did not get this error again in the last two days. I'll post a new
reply if I encounter it again. (It happened just a few times out of many
thousand program invocations)
 
Reply With Quote
 
Paul Rubin
Guest
Posts: n/a
 
      05-07-2011
Roy Smith <> writes:
> changes = [ ]
> for key in d.iterkeys():
> if is_bad(key):
> changes.append(key)


changes = list(k for k in d if is_bad(k))

is a little bit more direct.
 
Reply With Quote
 
Roy Smith
Guest
Posts: n/a
 
      05-07-2011
In article <>,
Paul Rubin <> wrote:

> Roy Smith <> writes:
> > changes = [ ]
> > for key in d.iterkeys():
> > if is_bad(key):
> > changes.append(key)

>
> changes = list(k for k in d if is_bad(k))
>
> is a little bit more direct.


This is true. I still file list comprehensions under "new fangled
toys". While I use them, and appreciate their value, I admit they're
not always the first thing that comes to my mind.

OBTW,

> changes = [k for k in d if is_bad(k)]


is even more direct
 
Reply With Quote
 
Hans Mulder
Guest
Posts: n/a
 
      05-08-2011
On 08/05/2011 00:12, Roy Smith wrote:
> In article<>,
> Paul Rubin<> wrote:
>
>> Roy Smith<> writes:
>>> changes = [ ]
>>> for key in d.iterkeys():
>>> if is_bad(key):
>>> changes.append(key)

>>
>> changes = list(k for k in d if is_bad(k))
>>
>> is a little bit more direct.

>
> This is true. I still file list comprehensions under "new fangled
> toys". While I use them, and appreciate their value, I admit they're
> not always the first thing that comes to my mind.
>
> OBTW,
>
>> changes = [k for k in d if is_bad(k)]

>
> is even more direct


How about:

changes = filter(is_bad, d)

Or would that be too compact?

-- HansM

 
Reply With Quote
 
Paul Rubin
Guest
Posts: n/a
 
      05-08-2011
Hans Mulder <> writes:
> How about:
> changes = filter(is_bad, d)
> Or would that be too compact?


I thought of writing something like that but filter in python 3 creates
an iterator that would have the same issue of walking the dictionary
while the dictionary is mutating.

changes = list(filter(is_bad, d))

should work.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
RuntimeError: dictionary changed size during iteration Robert Dailey Python 6 12-09-2008 06:25 PM
"RuntimeError: dictionary changed size during iteration" ; Good atomiccopy operations? robert Python 29 03-14-2006 06:22 AM
Re: "RuntimeError: dictionary changed size during iteration" ;Good atomic copy operations? Jean-Paul Calderone Python 0 03-13-2006 03:41 AM
Re: RuntimeError: dictionary changed size during iteration Terry Reedy Python 0 01-20-2005 06:40 PM
RuntimeError: dictionary changed size during iteration Roman Suzi Python 0 01-19-2005 08:45 PM



Advertisments