Right now, the following code is valid in Python 3.9 (and infinitely loops):

```
lst = [1, 2, 3]
for i in lst:
    lst.append(i)
```

IMO this is a source of bugs, and even though this behavior is technically 
documented (https://bugs.python.org/issue32767), I don't know if it should 
belong in the core language. For example, you cannot change the size of a set 
or dict during iteration; you will get a RuntimeError. Allegedly this is due to 
the underlying implementation of the dict/set iterators 
(https://bugs.python.org/issue22084) but I think it would be beneficial this 
was made part of the public API for list, set, and dict iterators.

Concretely, I'm proposing the following set of changes to Python:

1. If the size of a list, set, or dict changes, invalidate all existing 
iterators to that containers. Document this behavior as part of the public API.
2. Attempting to call next() on an invalidated iterator raises a RuntimeError.
3. Add a key_iter function to itertools (name up for debate), which replicates 
the existing functionality for lists. It could also make a copy of the keys of 
a set/dict, so you could use it to mutate those as well during iteration (to 
allow users to write functions that mutate an arbitrary duck-typed collection)
4. Gate (1, 2) for lists behind a __future__ flag with a long migration window 
(e.g. ~3.16).

Benefits:

1. Reduce a source of bugs (deleting while iterating forward through a list 
often means that your logic skips elements; appending can lead to infinite 
loops or unexpected behavior).
2. Make the iterator invalidation consistent between lists, sets, and dicts.
3. More consistent behavior with collections in other languages (C++, Java).

Drawbacks:

1. Migration costs.
  But mutating during iteration should be relatively rare, given that most 
people discourage mutating while iterating 
(https://google.github.io/styleguide/pyguide.html#284-decision, 
https://stackoverflow.com/a/1637875/1625484)
  __future__ feature flag plus deprecation warnings should go along way to help 
people migrate their code. We would also offer an easy in-place fix (key_iter).
2. You can't write algorithms that append to a list (e.g. BFS) with the normal 
list iterator.
  Again, you could use key_iter (or use a deque with block size == 1; see Open 
Questions).

Open Questions

Is (3) necessary? It's not hard to write an implementation yourself.
What should be done about deque for block sizes > 1? If this change gets made, 
should we add way to create a deque with block size == 1 whose iterators are 
never invalidated?
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/G53KTBQERD2ZWMLODBRLG4CIFHILF7NJ/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to