.popall() method on list/set/dict

In Python you often get problematic results if you modify a collection as you are iterating over it.

So we iterate over a copy, like

new_list = []
for x in original_list:
    if ...:
        new_list.append(x)
original_list[:] = new_list

In some situations you can just original_list = new_list but obviously not if other things have references to original_list, and if that variable isn’t from this scope you need a global or nonlocal declaration to assign to it.

What if lists/sets/dicts had a function like .popall() that removes and returns all items. You’d just be able to write:

for item in mylist.popall():
    if ...:
        mylist.append(item)

It gets around the problem of modifying a collection while iterating over it while being a couple of lines shorter.

It’s a cheap operation. It could just hand ownership of the backing array over to a new collection object without touching even the reference counts of the items it contains. If I have this right it’s actually a little cheaper - it is skipping one incref and one decref per object vs the list code I have above.

I typically prefer to use list comprehensions:

L[:] = [x for x in L if condition(x)]

This declarative/functional style lets you not have to worry about the mutable state of the list evolving over time and instead just lets you say what you want the list to look like.

If I understand though, you can already do what you’re suggesting as

L2 = L[:]
del L[:]
for x in L2:
    if ...:
        L.append(x)

I believe there was a thread a while back about adding a list method to do those first two lines in constant time, but it may have been generally opposed, I don’t remember.

1 Like

Isn’t this popall method just the same as:

newlist = oldlist[:]
oldlist[:] = []

except performed as a single operation?

Can we see some actual (i.e. real) example code where you want to modify
a list in place by adding or removing items?

Modifying the value in the same position is fine, there is no need to
iterate over a copy. But you do need the index:

for i, x in enumerate(mylist):
    if condition:
        mylist[i] = change(x)

For cases where you are filtering values from a list, using filter() or
a comprehension is easier than a loop over a copy with append:

mylist[:] = [x for x in mylist if cond]

I’m struggling to think of any occasions where I have needed to iterate
over a copy and modify the list by deleting or adding items:

for x in mylist[:]:
    if a: mylist.append(something)
    if b: del mylist[position]

At least, not since I was a complete newbie still trying to write Pascal
code in Python syntax. (Which, by the way, is not very successful.)

So I find it hard to think of when I would actually want to use this.
Without some real examples, can’t decide whether I think it is:

  1. redundant now that we have comprehensions;

  2. a solution in search of a problem.