Login | Register For Free | Help
Search for: (Advanced)

Mailing List Archive: Python: Python

can't delete from a dictionary in a loop

 

 

Python python RSS feed   Index | Next | Previous | View Threaded


upton at virginia

May 16, 2008, 2:22 PM

Post #1 of 14 (205 views)
Permalink
can't delete from a dictionary in a loop

This might be more information than necessary, but it's the best way I
can think of to describe the question without being too vague.

The task:

I have a list of processes (well, strings to execute said processes)
and I want to, roughly, keep some number N running at a time. If one
terminates, I want to start the next one in the list, or otherwise,
just wait.

The attempted solution:

Using subprocess, I Popen the next executable in the list, and store
it in a dictionary, with keyed on the pid:
(outside the loop)
procs_dict={}

(inside a while loop)
process = Popen(benchmark_exstring[num_started], shell=true)
procs_dict[process.pid]=process

Then I sleep for a while, then loop through the dictionary to see
what's terminated. For each one that has terminated, I decrement a
counter so I know how many to start next time, and then try to remove
the record from the dictionary (since there's no reason to keep
polling it since I know it's terminated). Roughly:

for pid in procs_dict:
if procs_dict[pid].poll() != None
# do the counter updates
del procs_dict[pid]

The problem:

RuntimeError: dictionary changed size during iteration

So, the question is: is there a way around this? I know that I can
just /not/ delete from the dictionary and keep polling each time
around, but that seems sloppy and like it could keep lots of memory
around that I don't need, since presumably the dictionary holding a
reference to the Popen object means the garbage collector could never
reclaim it. Is the only reasonable solution to do something like
append all of those pids to a list, and then after I've iterated over
the dictionary, iterate over the list of pids to delete?

(Also, from the implementation side, is there a reason the dictionary
iterator can't deal with that? If I was deleting from in front of the
iterator, maybe, but since I'm deleting from behind it...)
--
http://mail.python.org/mailman/listinfo/python-list


zephyrfalcon!NO_SPAM! at gmail

May 16, 2008, 2:28 PM

Post #2 of 14 (203 views)
Permalink
Re: can't delete from a dictionary in a loop [In reply to]

Dan Upton wrote:

> for pid in procs_dict:
> if procs_dict[pid].poll() != None
> # do the counter updates
> del procs_dict[pid]
>
> The problem:
>
> RuntimeError: dictionary changed size during iteration

I don't know if the setup with the pids in a dictionary is the best way to
manage a pool of processes... I'll leave it others, presumably more
knowledgable, to comment on that. :-) But I can tell you how to solve the
immediate problem:

for pid in procs_dict.keys():
...

Hope this helps!

--Hans
--
http://mail.python.org/mailman/listinfo/python-list


gherron at islandtraining

May 16, 2008, 2:33 PM

Post #3 of 14 (204 views)
Permalink
Re: can't delete from a dictionary in a loop [In reply to]

Dan Upton wrote:
> This might be more information than necessary, but it's the best way I
> can think of to describe the question without being too vague.
>
> The task:
>
> I have a list of processes (well, strings to execute said processes)
> and I want to, roughly, keep some number N running at a time. If one
> terminates, I want to start the next one in the list, or otherwise,
> just wait.
>
> The attempted solution:
>
> Using subprocess, I Popen the next executable in the list, and store
> it in a dictionary, with keyed on the pid:
> (outside the loop)
> procs_dict={}
>
> (inside a while loop)
> process = Popen(benchmark_exstring[num_started], shell=true)
> procs_dict[process.pid]=process
>
> Then I sleep for a while, then loop through the dictionary to see
> what's terminated. For each one that has terminated, I decrement a
> counter so I know how many to start next time, and then try to remove
> the record from the dictionary (since there's no reason to keep
> polling it since I know it's terminated). Roughly:
>
> for pid in procs_dict:
> if procs_dict[pid].poll() != None
> # do the counter updates
> del procs_dict[pid]
>
> The problem:
>
> RuntimeError: dictionary changed size during iteration
>
> So, the question is: is there a way around this?

Yes. Create a list of keys, and loop through it:
pids = procs_dict.keys()
for pid in pids:
if procs_dict[pid].poll() != None
# do the counter updates
del procs_dict[pid]

Then the diction delete operation won't trip up the loop and its
internal counter.



OR: Create a list of things to delete while you are in the loop, and do
the delete afterwards

deleteme = []
for pid in procs_dict:
if procs_dict[pid].poll() != None
# do the counter updates
deleteme.append(pid)

for pid in deleteme:
del procs_dict[pid]



OR: shred and rebuild the dictionary each time:

new_dict = {}
for pid,value in procs_dict.items():
if value.poll() != None:
# do the counter updates
pass
else:
new_dict[pid] = value

procs_dict = new_dict



Gary Herron


> I know that I can
> just /not/ delete from the dictionary and keep polling each time
> around, but that seems sloppy and like it could keep lots of memory
> around that I don't need, since presumably the dictionary holding a
> reference to the Popen object means the garbage collector could never
> reclaim it. Is the only reasonable solution to do something like
> append all of those pids to a list, and then after I've iterated over
> the dictionary, iterate over the list of pids to delete?
>
> (Also, from the implementation side, is there a reason the dictionary
> iterator can't deal with that? If I was deleting from in front of the
> iterator, maybe, but since I'm deleting from behind it...)
> --
> http://mail.python.org/mailman/listinfo/python-list
>

--
http://mail.python.org/mailman/listinfo/python-list


bruno.desthuilliers at gmail

May 16, 2008, 2:34 PM

Post #4 of 14 (204 views)
Permalink
Re: can't delete from a dictionary in a loop [In reply to]

On 16 mai, 23:28, Hans Nowak <zephyrfalcon!NO_SP...@gmail.com> wrote:
> Dan Upton wrote:
> > for pid in procs_dict:
> > if procs_dict[pid].poll() != None
> > # do the counter updates
> > del procs_dict[pid]
>
> > The problem:
>
> > RuntimeError: dictionary changed size during iteration
>
> I don't know if the setup with the pids in a dictionary is the best way to
> manage a pool of processes... I'll leave it others, presumably more
> knowledgable, to comment on that. :-) But I can tell you how to solve the
> immediate problem:
>
> for pid in procs_dict.keys():

I'm afraid this will do the same exact thing. A for loop on a dict
iterates over the dict keys, so both statements are strictly
equivalent from a practical POV.
--
http://mail.python.org/mailman/listinfo/python-list


bruno.desthuilliers at gmail

May 16, 2008, 2:38 PM

Post #5 of 14 (204 views)
Permalink
Re: can't delete from a dictionary in a loop [In reply to]

On 16 mai, 23:34, "bruno.desthuilli...@gmail.com"
<bruno.desthuilli...@gmail.com> wrote:
> On 16 mai, 23:28, Hans Nowak <zephyrfalcon!NO_SP...@gmail.com> wrote:
>
>
> > Dan Upton wrote:
> > > for pid in procs_dict:
(snip)
> > for pid in procs_dict.keys():
>
> I'm afraid this will do the same exact thing. A for loop on a dict
> iterates over the dict keys, so both statements are strictly
> equivalent from a practical POV.

Hem. Forget it. I should think twice before posting - this will
obviously make a big difference here. Sorry for the noise.

--
http://mail.python.org/mailman/listinfo/python-list


gherron at islandtraining

May 16, 2008, 2:40 PM

Post #6 of 14 (204 views)
Permalink
Re: can't delete from a dictionary in a loop [In reply to]

bruno.desthuilliers[at]gmail.com wrote:
> On 16 mai, 23:28, Hans Nowak <zephyrfalcon!NO_SP...@gmail.com> wrote:
>
>> Dan Upton wrote:
>>
>>> for pid in procs_dict:
>>> if procs_dict[pid].poll() != None
>>> # do the counter updates
>>> del procs_dict[pid]
>>>
>>> The problem:
>>>
>>> RuntimeError: dictionary changed size during iteration
>>>
>> I don't know if the setup with the pids in a dictionary is the best way to
>> manage a pool of processes... I'll leave it others, presumably more
>> knowledgable, to comment on that. :-) But I can tell you how to solve the
>> immediate problem:
>>
>> for pid in procs_dict.keys():
>>

No, keys() produces a list (which is what is wanted here).

It's iterkeys() that produces an iterator which would reproduce the OP's
problem.

And then, in Python3, keys() produces something else altogether (call a
view of the dictionary) which would provoke the same problem, so yet
another solution would have to be found then.

Gary Herron

>
> I'm afraid this will do the same exact thing. A for loop on a dict
> iterates over the dict keys, so both statements are strictly
> equivalent from a practical POV.
> --
> http://mail.python.org/mailman/listinfo/python-list
>

--
http://mail.python.org/mailman/listinfo/python-list


zephyrfalcon!NO_SPAM! at gmail

May 16, 2008, 2:51 PM

Post #7 of 14 (204 views)
Permalink
Re: can't delete from a dictionary in a loop [In reply to]

bruno.desthuilliers[at]gmail.com wrote:
> On 16 mai, 23:34, "bruno.desthuilli...@gmail.com"
> <bruno.desthuilli...@gmail.com> wrote:
>> On 16 mai, 23:28, Hans Nowak <zephyrfalcon!NO_SP...@gmail.com> wrote:
>>
>>
>>> Dan Upton wrote:
>>>> for pid in procs_dict:
> (snip)
>>> for pid in procs_dict.keys():
>> I'm afraid this will do the same exact thing. A for loop on a dict
>> iterates over the dict keys, so both statements are strictly
>> equivalent from a practical POV.
>
> Hem. Forget it. I should think twice before posting - this will
> obviously make a big difference here. Sorry for the noise.

:-) It appears that you would be right if this was Python 3.0, though:

Python 3.0a5 (r30a5:62856, May 16 2008, 11:43:33)
[GCC 4.0.1 (Apple Computer, Inc. build 5367)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> d = {1: 2, 3: 4, 5: 6}
>>> for i in d.keys(): del d[i]
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

Maybe 'for i in d' and 'for i in d.keys()' *are* functionally equivalent in 3.0,
as d.keys() returns an object that iterates over d's keys... but I haven't read
enough about it yet to be sure. In any case, the problem goes away when we
force a list:

>>> d = {1: 2, 3: 4, 5: 6}
>>> for i in list(d.keys()): del d[i]
...
>>> d
{}

--Hans
--
http://mail.python.org/mailman/listinfo/python-list


castironpi at gmail

May 16, 2008, 3:19 PM

Post #8 of 14 (204 views)
Permalink
Re: can't delete from a dictionary in a loop [In reply to]

On May 16, 4:51 pm, Hans Nowak <zephyrfalcon!NO_SP...@gmail.com>
wrote:
> bruno.desthuilli...@gmail.com wrote:
> > On 16 mai, 23:34, "bruno.desthuilli...@gmail.com"
> > <bruno.desthuilli...@gmail.com> wrote:
> >> On 16 mai, 23:28, Hans Nowak <zephyrfalcon!NO_SP...@gmail.com> wrote:
>
> >>> Dan Upton wrote:
> >>>> for pid in procs_dict:
> > (snip)
> >>>    for pid in procs_dict.keys():
> >> I'm afraid this will do the same exact thing. A for loop on a dict
> >> iterates over the dict keys, so both statements are strictly
> >> equivalent from a practical POV.
>
> > Hem. Forget it. I should think twice before posting - this will
> > obviously make a big difference here.  Sorry for the noise.
>
> :-)  It appears that you would be right if this was Python 3.0, though:
>
> Python 3.0a5 (r30a5:62856, May 16 2008, 11:43:33)
> [GCC 4.0.1 (Apple Computer, Inc. build 5367)] on darwin
> Type "help", "copyright", "credits" or "license" for more information.
>  >>> d = {1: 2, 3: 4, 5: 6}
>  >>> for i in d.keys(): del d[i]
> ...
> Traceback (most recent call last):
>    File "<stdin>", line 1, in <module>
> RuntimeError: dictionary changed size during iteration
>
> Maybe 'for i in d' and 'for i in d.keys()' *are* functionally equivalent in 3.0,
> as d.keys() returns an object that iterates over d's keys... but I haven't read
> enough about it yet to be sure.  In any case, the problem goes away when we
> force a list:
>
>  >>> d = {1: 2, 3: 4, 5: 6}
>  >>> for i in list(d.keys()): del d[i]
> ...
>  >>> d
> {}
>
> --Hans- Hide quoted text -
>
> - Show quoted text -

You may be searching for:

for i in d.keys()[:]:
del d[ i ]

--
http://mail.python.org/mailman/listinfo/python-list


eduardo.padoan at gmail

May 16, 2008, 3:38 PM

Post #9 of 14 (204 views)
Permalink
Re: can't delete from a dictionary in a loop [In reply to]

On Fri, May 16, 2008 at 6:40 PM, Gary Herron <gherron[at]islandtraining.com> wrote:
> bruno.desthuilliers[at]gmail.com wrote:
>>
>> On 16 mai, 23:28, Hans Nowak <zephyrfalcon!NO_SP...@gmail.com> wrote:
>>
>>>
>>> Dan Upton wrote:
>>>
>>>>
>>>> for pid in procs_dict:
>>>> if procs_dict[pid].poll() != None
>>>> # do the counter updates
>>>> del procs_dict[pid]
>>>> The problem:
>>>> RuntimeError: dictionary changed size during iteration
>>>>
>>>
>>> I don't know if the setup with the pids in a dictionary is the best way
>>> to
>>> manage a pool of processes... I'll leave it others, presumably more
>>> knowledgable, to comment on that. :-) But I can tell you how to solve
>>> the
>>> immediate problem:
>>>
>>> for pid in procs_dict.keys():
>>>
>
> No, keys() produces a list (which is what is wanted here).
> It's iterkeys() that produces an iterator which would reproduce the OP's
> problem.
>
> And then, in Python3, keys() produces something else altogether (call a view
> of the dictionary) which would provoke the same problem, so yet another
> solution would have to be found then.

In Python 3.0, list(procs_dict.keys()) would have the same effect.



> Gary Herron
>
>>
>> I'm afraid this will do the same exact thing. A for loop on a dict
>> iterates over the dict keys, so both statements are strictly
>> equivalent from a practical POV.
>> --
>> http://mail.python.org/mailman/listinfo/python-list
>>
>
> --
> http://mail.python.org/mailman/listinfo/python-list
>



--
Eduardo de Oliveira Padoan
http://www.advogato.org/person/eopadoan/
http://twitter.com/edcrypt
Bookmarks: http://del.icio.us/edcrypt
--
http://mail.python.org/mailman/listinfo/python-list


tjreedy at udel

May 16, 2008, 4:46 PM

Post #10 of 14 (203 views)
Permalink
Re: can't delete from a dictionary in a loop [In reply to]

"Dan Upton" <upton[at]virginia.edu> wrote in message
news:5504f9ac0805161422r31f3a0d6sbe4e49914ade7383[at]mail.gmail.com...
| RuntimeError: dictionary changed size during iteration

If you do not actually need a dict, an explicitly managed list is an
[untested]alternative.

maxproc = #
procs = []
while True:
if len(procs) < maxproc: [populate procs up to maxproc]
i, nprocs = 0, len(procs)
if not nprocs: break
while i < nprocs:
if terminated(procs[i]):
del procs[i]
nprocs -= 1
else:
i += 1

I am assuming that maxproc is low enough that the O(n) behavior of deletion
is inconsequential.

tjr



--
http://mail.python.org/mailman/listinfo/python-list


google at mrabarnett

May 16, 2008, 6:21 PM

Post #11 of 14 (201 views)
Permalink
Re: can't delete from a dictionary in a loop [In reply to]

On May 16, 10:22 pm, "Dan Upton" <up...@virginia.edu> wrote:
> This might be more information than necessary, but it's the best way I
> can think of to describe the question without being too vague.
>
> The task:
>
> I have a list of processes (well, strings to execute said processes)
> and I want to, roughly, keep some number N running at a time. If one
> terminates, I want to start the next one in the list, or otherwise,
> just wait.
>
> The attempted solution:
>
> Using subprocess, I Popen the next executable in the list, and store
> it in a dictionary, with keyed on the pid:
> (outside the loop)
> procs_dict={}
>
> (inside a while loop)
> process = Popen(benchmark_exstring[num_started], shell=true)
> procs_dict[process.pid]=process
>
> Then I sleep for a while, then loop through the dictionary to see
> what's terminated. For each one that has terminated, I decrement a
> counter so I know how many to start next time, and then try to remove
> the record from the dictionary (since there's no reason to keep
> polling it since I know it's terminated). Roughly:
>
> for pid in procs_dict:
> if procs_dict[pid].poll() != None
> # do the counter updates
> del procs_dict[pid]
>
> The problem:
>
> RuntimeError: dictionary changed size during iteration
>
> So, the question is: is there a way around this? I know that I can
> just /not/ delete from the dictionary and keep polling each time
> around, but that seems sloppy and like it could keep lots of memory
> around that I don't need, since presumably the dictionary holding a
> reference to the Popen object means the garbage collector could never
> reclaim it. Is the only reasonable solution to do something like
> append all of those pids to a list, and then after I've iterated over
> the dictionary, iterate over the list of pids to delete?
>
> (Also, from the implementation side, is there a reason the dictionary
> iterator can't deal with that? If I was deleting from in front of the
> iterator, maybe, but since I'm deleting from behind it...)
>
Why do you need a counter? len(procs_dict) will tell you how many are
in the dictionary.

You can rebuild the dictionary, excluding those that are no longer
active, with:

procs_dict = dict((id, process) for id, process in
procs_dict.iteritems() if process.poll() != None)

and then start N - len(procs_dict) new processes.
--
http://mail.python.org/mailman/listinfo/python-list


george.sakkis at gmail

May 17, 2008, 1:06 AM

Post #12 of 14 (186 views)
Permalink
Re: can't delete from a dictionary in a loop [In reply to]

On May 16, 5:22 pm, "Dan Upton" <up...@virginia.edu> wrote:
> This might be more information than necessary, but it's the best way I
> can think of to describe the question without being too vague.
>
> The task:
>
> I have a list of processes (well, strings to execute said processes)
> and I want to, roughly, keep some number N running at a time.  If one
> terminates, I want to start the next one in the list, or otherwise,
> just wait.
>
> The attempted solution:
>
> Using subprocess, I Popen the next executable in the list, and store
> it in a dictionary, with keyed on the pid:
> (outside the loop)
> procs_dict={}
>
> (inside a while loop)
> process = Popen(benchmark_exstring[num_started], shell=true)
> procs_dict[process.pid]=process
>
> Then I sleep for a while, then loop through the dictionary to see
> what's terminated.  For each one that has terminated, I decrement a
> counter so I know how many to start next time, and then try to remove
> the record from the dictionary (since there's no reason to keep
> polling it since I know it's terminated).  Roughly:
>
> for pid in procs_dict:
>   if procs_dict[pid].poll() != None
>    # do the counter updates
>    del procs_dict[pid]

Since you don't look up processes by pid, you don't need a dictionary
here. A cleaner and efficient solution is use a deque to pop processes
from one end and push them to the other if still alive, something like
this:

from collections import deque

processes = deque()
# start processes and put them in the queue

while processes:
for i in xrange(len(processes)):
p = processes.pop()
if p.poll() is None: # not finished yet
processes.append_left(p)
time.sleep(5)


HTH,
George
--
http://mail.python.org/mailman/listinfo/python-list


castironpi at gmail

May 17, 2008, 1:33 AM

Post #13 of 14 (186 views)
Permalink
Re: can't delete from a dictionary in a loop [In reply to]

On May 17, 3:06 am, George Sakkis <george.sak...@gmail.com> wrote:
> On May 16, 5:22 pm, "Dan Upton" <up...@virginia.edu> wrote:
>
>
>
>
>
> > This might be more information than necessary, but it's the best way I
> > can think of to describe the question without being too vague.
>
> > The task:
>
> > I have a list of processes (well, strings to execute said processes)
> > and I want to, roughly, keep some number N running at a time.  If one
> > terminates, I want to start the next one in the list, or otherwise,
> > just wait.
>
> > The attempted solution:
>
> > Using subprocess, I Popen the next executable in the list, and store
> > it in a dictionary, with keyed on the pid:
> > (outside the loop)
> > procs_dict={}
>
> > (inside a while loop)
> > process = Popen(benchmark_exstring[num_started], shell=true)
> > procs_dict[process.pid]=process
>
> > Then I sleep for a while, then loop through the dictionary to see
> > what's terminated.  For each one that has terminated, I decrement a
> > counter so I know how many to start next time, and then try to remove
> > the record from the dictionary (since there's no reason to keep
> > polling it since I know it's terminated).  Roughly:
>
> > for pid in procs_dict:
> >   if procs_dict[pid].poll() != None
> >    # do the counter updates
> >    del procs_dict[pid]
>
> Since you don't look up processes by pid, you don't need a dictionary
> here. A cleaner and efficient solution is use a deque to pop processes
> from one end and push them to the other if still alive, something like
> this:
>
> from collections import deque
>
> processes = deque()
> # start processes and put them in the queue
>
> while processes:
>     for i in xrange(len(processes)):
>         p = processes.pop()
>         if p.poll() is None: # not finished yet
>             processes.append_left(p)
>     time.sleep(5)
>
> HTH,
> George- Hide quoted text -
>
> - Show quoted text -

No underscore in appendleft.
--
http://mail.python.org/mailman/listinfo/python-list


Scott.Daniels at Acm

May 17, 2008, 9:25 PM

Post #14 of 14 (175 views)
Permalink
Re: can't delete from a dictionary in a loop [In reply to]

Eduardo O. Padoan wrote:
> On Fri, May 16, 2008 at 6:40 PM, Gary Herron <gherron[at]islandtraining.com> wrote:
>> bruno.desthuilliers[at]gmail.com wrote:
>>> On 16 mai, 23:28, Hans Nowak <zephyrfalcon!NO_SP...@gmail.com> wrote:
>>>
>>>> Dan Upton wrote:
<<asking about iterating over a dictionary and deleting elements>>
>>>> ...to solve the immediate problem:
>>>> for pid in procs_dict.keys():
>> And then, in Python3, keys() produces something else altogether (call a view
>> of the dictionary) which would provoke the same problem, so yet another
>> solution would have to be found then.
> In Python 3.0, list(procs_dict.keys()) would have the same effect.
Or (simpler in either 3.0 or 2.X):
for pid in list(procs_dict):
...

--Scott David Daniels
Scott.Daniels[at]Acm.Org
--
http://mail.python.org/mailman/listinfo/python-list

Python python RSS feed   Index | Next | Previous | View Threaded
 
 


Interested in having your list archived? Contact lists@gossamer-threads.com
 
  Web Applications & Managed Hosting Powered by Gossamer Threads Inc.