'Is it possible to apply an operation "in place" to every item of a set in Python?

I would like to remove a prefix (if present) in a set of unique names.

unique_names = {'A_first','A_second','A_third','other'}

One way to do this, is to convert the set into a list:

unique_names = list(unique_names)

for i, name in enumerate(unique_names):
    if name.startswith('A_'):
        unique_names[i] = name[2:]   

Another way, could be creating a new set like explained here: Apply a operation to every item in a set in Python

However both solutions require to create a new object. Is it possible to do this "in place", without creating a new object? If not, what is the most efficient way to apply the operation to every item?



Solution 1:[1]

You will have 2 problems here:

  1. you are not allowed to change a container while iterating it, and a set has no direct accessor (no __getitem__ method)
  2. strings are immutable

Per 1. you cannot replace an element while iterating the container, and per 2. you cannot mutate the contained object.

That means that you will have to build a new container and replace the container as a whole. The Pythonic way would be to use a set comprehension as shown in the answer to the linked question, or by using @Thy's answer

Solution 2:[2]

Update:
@user2357112 supports Monica found an example which verifies that modifying a set during iteration is still unsafe in Python 3. In that case, the easiest thing to do is to build a new set.

new_unique = {k[2:] if k.startswith('A_') else k for k in unique_names}

If you need to update the set in place (say, you are updating a set passed as a parameter), you may as well just clear the set and update it with the new one. For example,

def update_set(s):
    new_unique = {k[2:] if k.startswith('A_') else k for k in s}
    s.clear()
    s.update(new_unique)

update_set(unique_names)

Since strings are immutable, you have to remove the old string and add its replacement to the original set. You can try

for x in unique_names:
    if x.startswith('A_'):
        unique_names.add(x[2:])
        unique_names.remove(x)

In Python 2, you would get a RuntimeError: Set changed size during iteration. That doesn't appear to be the case in Python 3, or at least in Python 3.9. I'm not entirely convinced that this is guaranteed to be correct, though.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Serge Ballesta
Solution 2